如上图所示,Javascript执行引擎的主线程运行的时候,产生堆(heap)和栈(stack),程序中代码依次进入栈中等待执行,javascript
若执行时遇到异步方法,该异步方法会被添加到用于回调的队列(queue)中【即JavaScript执行引擎的主线程拥有一个执行栈/堆和一个任务队列】。前端
栈(stack) : 函数调用会造成了一个堆栈帧
堆(heap) : 对象被分配在一个堆中,一个用以表示一个内存中大的未被组织的区域。
队列(queue) : 一个 JavaScript 运行时包含了一个待处理的消息队列。每个消息都与一个函数相关联。java当栈为空时,则从队列中取出一个消息进行处理。这个处理过程包含了调用与这个消息相关联的函数(以及于是建立了一个初始堆栈帧)。web
当栈再次为空的时候,也就意味着该消息处理结束。 浏览器
首先,咱们对图中的一些名词稍加解释:并发
【注:由于主线程从"任务队列"中读取事件的过程是循环不断的,所以这种运行机制又称为Event Loop(事件循环)】异步
下面咱们经过setTimeout来看看单线程的JavaScript执行引擎是如何来执行该方法的。svg
console.log(1); setTimeout(function() { console.log(2); },5000); console.log(3); //输出结果: //1 //3 //2复制代码
基本上,一个完整的事件循环模型就讲完了。如今咱们来重点关注一下队列。
异步任务分为两种:Macrotasks 和 Microtasks。函数
Macrotasks 和 Microtasks有什么区别呢?咱们以setTimeout和Promises来举例。oop
console.log('1'); setTimeout(function() { console.log('2'); }, 0); Promise.resolve().then(function() { console.log('3'); }).then(function() { console.log('4'); }); console.log('5'); //输出结果: //1 //5 //3 //4 //2复制代码
缘由是Promise中的then方法的函数会被推入 microtasks 队列,而setTimeout的任务会被推入 macrotasks 队列。在每一次事件循环中,macrotask 只会提取一个执行,而 microtask 会一直提取,直到 microtasks 队列清空。
结论以下:
【注:通常状况下,macrotask queues 咱们会直接称为 task queues,只有 microtask queues 才会特别指明。】