今天看到一道题蛮有意思的。题目是这样的:
setTimeout(function() {
console.log(1)
}, 0);
new Promise(function executor(resolve) {
console.log(2);
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve();
}
console.log(3);
}).then(function() {
console.log(4);
});
console.log(5);
这段代码的输出是什么?
一开始我没细想,以为是2,3,5,1,4
,然后我随手就在chrome的console中运行了一下,发现结果是2,3,5,4,1
。
略微惊讶,,但是随即我想到setTimeout函数好像有个不成文的规定,当time为0时会延迟4ms运行(现在似乎已经写进了标准),这样一想,好像是该是这个结果哦。。
但是我发现了个小问题:
这个console中的结果有个undefined。
一般来讲,这个undefined应该是我这段代码运行完了的返回值啊。。
那么它应该出现在4前面啊!为什么出现在1前面了呢??
带着这个疑问我去搜索了下,发现我不知道的还有蛮多。。。
这个事情要从Event Loop说起。
我们都知道JavaScript是单线程的(虽然web worker能进行多线程的计算),它有个经典的Event Loop事件模型,而实际上就当前的这个程序来说,可以看做有两个任务队列。一个叫macro-task,另一个叫micro-task。根据Promises/A+规范:
- macro-task包括:
script(整体代码)
,setTimeout
,setInterval
,setImmediate
,I/O
,UI rendering
- micro-task包括:
process.nextTick
,Promises(这里指浏览器实现的原生 Promise)
,Object.observe(已废弃)
,MutationObserver
也就是说,浏览器运行这段程序的时候,经历了以下几个步骤:
- 将这段代码放到一个macro-task任务队列中,然后执行
- 执行到setTimeout,将这个setTimeout放到了当前的这个macro-task中,然后继续执行任务
- 执行到了promise,执行promise中的代码,输出2和3,将then回调放入micro-task,然后继续执行
- 输出5,至此执行完了macro-task中的第一个任务,然后执行micro-task中的全部任务,输出了4
- 至此,这段代码执行完毕,输出返回值undefined
- 执行macro-task中的下一个任务,输出1.
所以才看到了那样的结果。
更多内容请参考:知乎问题