JS异步执行顺序
今天在重温promise的时候,突然发现了一个有趣的题目:
(function() {
setTimeout(() => {
console.log(0);
});
new Promise(resolve => {
console.log(1);
setTimeout(() => {
resolve();
Promise.resolve().then(() => {
console.log(2);
setTimeout(() => console.log(3));
Promise.resolve().then(() => console.log(4));
});
});
Promise.resolve().then(() => console.log(5));
}).then(() => {
console.log(6);
Promise.resolve().then(() => console.log(7));
setTimeout(() => console.log(8));
});
console.log(9);
})();
我们先放上结果:
1 9 5 0 6 2 7 4 8 3
简要讲述
首先我们知道JS分为了同步和异步,并且是顺序是先同步后异步,也就是同步代码执行完成后,哪怕异步代码到了它执行的时候,也会先让同步执行完。但是JS的执行顺序还可以分得更细,那就是异步的顺序。
我们知道很多异步任务,比如:
setTimeout, setInterval, setImmediate, Promises.then, Promise.catch
甚至还有nodejs中的process.nextTick等,就像我们知道process.nextTick比promise先执行一样。那么这些异步事件是谁先执行谁后执行呢。
这涉及到了事件循环(event loop)。事件循环也就是事件出入栈。
上面说了这么多异步任务,我们先把它们分为两类:
宏任务(macro-task):setTimeout, setInterval, setImmediate
微任务(micro-task):Promises.then, Promise.catch
然后我们再明白一个概念:先执行微任务,再执行宏任务。这种执行是,先执行清空微任务队列,再执行宏任务,当当前宏任务执行完后,微任务队列中又有微任务,再次将微任务队列执行完,再执行下一个宏任务。
在异步任务执行过程中遇到宏任务与微任务,将其依次放入当前事件循环队列中。
例题描述
首先执行主线程,也就是script(script是宏任务),因为此时没有微任务
然后遇到setTimeout(() => { console.log(0);});
这是宏任务,将其放入宏任务队列中 记为 – 宏任务1
promise新建后会立即执行,输出1
然后将
setTimeout(() => {
resolve();
Promise.resolve().then(() => {
console.log(2);
setTimeout(() => console.log(3));
Promise.resolve().then(() => console.log(4));
});
});
继续放入宏任务队列中 记为 – 宏任务2
将Promise.resolve().then(() => console.log(5));
放入微任务队列中 记为 – 微任务1
继续往下走,输出9
此时当前宏任务执行完毕,开始执行微任务队列,目前微任务队列中只有微任务1,执行,输出5
微任务队列此时清空,然后执行宏任务1,输出0
微任务队列依然没有任务,继续执行宏任务2,遇见 resolve();
,于是将
.then(() => {
console.log(6);
Promise.resolve().then(() => console.log(7));
setTimeout(() => console.log(8));
});
console.log(9);
})
放入微任务队列中,记为 – 微任务2,再将
Promise.resolve().then(() => {
console.log(2);
setTimeout(() => console.log(3));
Promise.resolve().then(() => console.log(4));
});
记为 – 微任务3
当前宏任务执行完毕,开始执行微任务,先执行微任务2,输出6
;将Promise.resolve().then(() => console.log(7));
记为 – 微任务4;setTimeout(() => console.log(8));
记为 – 宏任务3
执行微任务3,输出2
;将setTimeout(() => console.log(3));
记为 – 宏任务4;将Promise.resolve().then(() => console.log(4));
记为 – 微任务5;
此时队列中依然有微任务,执行微任务4,输出7
;执行微任务5,输出4
;微任务队列执行完毕
执行宏任务3,输出8
;执行宏任务4,输出3
;全部执行完毕
那么我们的结果为1 9 5 0 6 2 7 4 8 3