<twoyao>

March 01, 2018

一些”没劲”的面试官经常喜欢给出一段代码,然后问输出什么,比如这样(open in ramda)

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

new Promise((resolve, reject) => {
  console.log('resolving....')
  resolve(0)
}).then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');

要弄清楚究竟如何,先要理解JS中的事件循环。总所周知,JS是单线程架构,这是它最强也最常被人诟病的特点。Anyway,在Event Loop中,引擎每次从macrotasks queue中取出一个进行执行, 当marcrotask执行完后,再逐个执行microtasks queue(FIFO队列)中的任务,在执行期间如果产生了其他的microtask,则会被追加到microtasks queue的队尾,直到队列为空。

如果microtask递归地创建microtask,则有可能会导致阻塞。Node.js为避免这种情况,如果连续处理超过process.maxTickDepth(1000)个microtask之后将强制执行下一个macrotask。也就是说microtask是比macrotask更轻量,更快被调度的任务。setTimeout, setInterval, setImmediate, requestAnimationFrame 这些调用会产生macrotask;process.nextTick, Promise.then则会产生microtask。

回到问题本身,Promise构造函数之后会马上执行doResolve(见promsie的一个实现),然后退出继续往下执行。then方法则产生一个microtask。所以上述例子先输出setTimout,然后进入到Promise中输出resolving,接着退出输出script end。 两个then产生了2个microtask会先于setTimeout执行,输出promise1,promise2。最后执行macrotask中的setTimeout。

最后输出如下

script start
resolving....
script end
promise1
promise2
setTimeout