[JavaScript] Macrotask Queue和Microtask Queue

96
何幻
2016.03.07 07:16* 字数 378
setImmediate(function(){
    console.log(1);
},0);
setTimeout(function(){
    console.log(2);
},0);
new Promise(function(resolve){
    console.log(3);
    resolve();
    console.log(4);
}).then(function(){
    console.log(5);
});
console.log(6);
process.nextTick(function(){
    console.log(7);
});
console.log(8);
结果是:3 4 6 8 7 5 2 1

事件的注册顺序如下:

setImmediate - setTimeout - promise.then - process.nextTick

因此,我们得到了优先级关系如下:

process.nextTick > promise.then > setTimeout > setImmediate

至于为什么promise.then比setTimeout优先执行,原因如下:

Promise/A+规范指出:
Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.

V8实现中,两个队列各包含不同的任务:

macrotasks: script(整体代码),setTimeout, setInterval, setImmediate, I/O, UI rendering
microtasks: process.nextTick, Promises, Object.observe, MutationObserver

执行过程如下:
JavaScript引擎首先从macrotask queue中取出第一个任务
执行完毕后,将microtask queue中的所有任务取出,按顺序全部执行
然后再从macrotask queue中取下一个
执行完毕后,再次将microtask queue中的全部取出;
循环往复,直到两个queue中的任务都取完。

解释:
代码开始执行时,所有这些代码在macrotask queue中,取出来执行之。
后面遇到了setTimeout,又加入到macrotask queue中,
然后,遇到了promise.then放入到了另一个队列microtask queue
等整个execution context stack执行完后,
下一步该取的是microtask queue中的任务了。
因此promise.then的回调比setTimeout先执行。


参考

8.1.4 Event loops - Perform a microtask checkpoint

2015.11
Web note ad 1