由一道面試題引起的思考。面試
setTimeout(function() {
console.log(4);
}, 0);
new Promise(function(reslove) {
console.log(1);
reslove();
}).then(function(data) {
console.log(3);
});
console.log(2);
複製代碼
會輸出:1,2,3,4。咱們來想一下爲何。ajax
瀏覽器中的事件循環 eventLoop,分爲同步執行棧和異步隊列,首先會執行同步的任務,當同步任務執行完以後會從異步隊列中取異步任務拿到同步執行棧中進行執行。promise
在取異步隊列時,還會有一個區分,就是區分微任務和宏任務。瀏覽器
由於微任務的優先級較高,因此會先將微任務的異步任務取出來進行執行,當微任務的任務都執行完畢以後,會將宏任務中的任務取出來執行。異步
咱們此次來看一下上面的題,promise 中是同步任務,promise 的 .then 中是異步任務,而且是微任務。使用 setTimeout 是宏任務,即便是延時爲 0,也是宏任務。oop
因此上面的執行順序就是先將 setTimeout 加入到異步隊列的宏任務池中,而後執行 promise 中的console.log(1)
,再將 promise 的.then 加到異步隊列中微任務池中,再執行同步任務console.log(2)
,當同步任務都執行完以後,去微任務中的任務,執行console.log(3)
,微任務執行完以後取宏任務,執行console.log(4)
。因此順序就是:1,2,3,4。post
擴展:ui
將這道面試題進行一些改造:spa
setTimeout(function() {
console.log(4);
}, 0);
new Promise(function(reslove) {
console.log(1);
setTimeout(function() {
reslove('done');
}, 0);
reslove('first');
}).then(function(data) {
console.log(data);
});
console.log(2);
複製代碼
這個時候就會輸出:1,2,first,4,沒有輸出 done,有些人可能會想,應該輸出 1,2,first,4,done,這個時候你就要知道,當使用 reslove 以後,promise 的狀態就從 pedding 變成了 resolve,promise 的狀態更改以後就不能再更改了,因此 reslove('done')
就不會執行了。code
當咱們把 reslove('done')
改爲 console.log('done')
的時候,他就會輸出 1,2,first,4,done 了。
再作一個更復雜的變動:
setTimeout(function() {
console.log(1);
}, 0);
new Promise(function(reslove) {
console.log(2);
reslove('p1');
new Promise(function(reslove) {
console.log(3);
setTimeout(function() {
reslove('setTimeout2');
console.log(4);
}, 0);
reslove('p2');
}).then(function(data) {
console.log(data);
});
setTimeout(function() {
reslove('setTimeout1');
console.log(5);
}, 0);
}).then(function(data) {
console.log(data);
});
console.log(6);
複製代碼
輸出的結果是:2,3,6,p2,p1,1,4,5。
先執行同步的任務,new Promise 中的都是同步任務,因此先輸出 2,3,6,而後再執行微任務的,微任務能夠插隊,因此並非先定義的 p1 先執行,並且先將 p2 執行,而後執行 p1,當微任務都執行完成以後,執行宏任務,宏任務依次輸出 1,4,5,promise 的狀態不能夠變動,因此 setTimeout1 和 setTimeout2 不會輸出。