【Javascript】探究javascript中的堆/栈/任务队列与并发模型 event loop的关系

堆/栈/队列

在javascript中,存在调用栈 (call stack)内存堆(memory heap) ,程序中函数依次进入栈中等待执行,若执行时遇到异步方法,该异步方法会被添加到用于回调的任务队列(task queue)中,【即JavaScript执行引擎的单线程拥有一个调用栈、内存堆和一个任务队列】javascript

调用栈 (call stack):CallStack是用来处理函数调用与返回的。特色是先进后出,每次调用一个函数,Javascript运行时会生成一个新的调用结构压入CallStack。而函数调用结束返回时,JavaScript运行时会将栈顶的调用结构弹出。因为栈的LIFO特性,每次弹出的必然是最新调用的那个函数的结构。函数调用会造成了一个堆栈帧,存放基本数据类型的变量java

内存堆(memory head):引用数据类型被存放在堆中,在咱们进行浅复制时,咱们改变的只是引用数据类型在栈内存中的引用地址,实际上它在堆内存中的引用地址仍然没有发生变化git

任务队列(task queue):javaScript 运行时包含了一个待处理的任务队列。github

并发模型 与 EventLoop

javascript引擎是单线程的,它的并发模型基于Event Loop(事件循环)算法

当线程中的同步任务执行完,执行栈为空时,则从任务队列(task queue)中取出异步任务进行处理。这个处理过程包含了调用与这个任务相关联的函数(以及于是建立了一个初始堆栈帧)。当执行栈再次为空的时候,也就意味着该任务处理结束,从任务队列中取出下一个异步任务进行处理,不断重复,这个过程是循环不断的, 因此整个的这种运行机制又称为Event Loop(事件循环).promise

Task Queue 任务队列

任务队列有宏任务队列微任务队列,

  • 宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering.
  • 微任务:process.nextTick, Promise, Object.observer, MutationObserver.
每次事件循环的时候:
  • 微任务/宏任务在相同做用域下,会先执行微任务,再执行宏任务
setTimeout(()=>{
        console.log('timer')
      })
      Promise.resolve('promise').then((res)=>{
        console.log(res)
      })
复制代码

  • 宏任务处于微任务做用域下,会先执行微任务,再执行微任务中的宏任务
Promise.resolve('promise').then((res)=>{
        console.log(res)
        setTimeout(()=>{
          console.log('timer')
        })
      })
复制代码

  • 微任务处于宏任务做用域下时,会先执行宏任务队列中的任务,而后再执行微任务队列中的任 务,在当前的微任务队列没有执行完成时,是不会执行下一个宏任务的。
Promise.resolve('promise1').then((res)=>{
          console.log(res)
          Promise.resolve('promise2').then((res)=>{
            console.log(res)
            Promise.resolve('promise3').then((res)=>{
              console.log(res)
            })
          })
          setTimeout(()=>{
            console.log('timer')
          })
        })
复制代码

Event loop 在浏览器端与NodeJS中的差异 以及 NodeJS中关于setTimeout与setImmediate引起的问题浏览器

若是个人文章对你有帮助,欢迎关注个人博客,JS/Python/算法系列,码不停题!!!