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