借鑒:
《Javascript 忍者秘籍》第二版,事件循環篇
面試 | JS 事件循環 event loop 經典面試題含答案 - 知乎 (zhihu.com)
概念
- 主棧隊列就是一個宏任務,每一個宏任務執行完就會執行宏任務中的微任務,直到微任務全部都執行完,才開始執行下一個宏任務。
- JS 中任務的執行順序優先級是:主棧全局任務(宏任務) > 宏任務中的微任務 > 下一個宏任務。,所以?
promise(微任務).then()
?的執行順序優先級高于setTimeout
定時器。 - 不能滿目的將?
.then
?的回調放入微任務隊列;因為沒有調用?resolve或者reject
?之前是不算異步任務完成的, 所以不能將回調隨意的放入微任務事件隊列 await
?是一個讓出線程的標志。await
?后面的表達式會先執行一遍,將?await
?后面的代碼加入到?micro task
中這個微任務是?promise
?隊列中微任務,然后就會跳出整個?async
?函數來繼續執行后面的代碼。process.nextTick
?是一個獨立于?eventLoop
?的任務隊列,主棧中的宏任務每一次結束后都是先執行?process.nextTick
隊列,在執行微任務?promise
?的?.then()
。- 每一個宏任務和宏任務的微任務執行完后都會對頁面 UI 進行渲染
- 宏任務 macrotask
- script 整體代碼
- setTimeout
- setInterval
- setImmediate
- I/O
- ui render
- 微任務 microtask
- process.nextTick
- promise.then
- await 后面的代碼
- MutationObserver(h5新特性)
為什么 await 后面的代碼會進入到promise
隊列中的微任務?
async/await 只是操作 promise 的語法糖,最后的本質還是promise
async function async1() {console.log('async1 start');await async2();console.log('async1 end');
}
// 上面的代碼等價于 ==>
async function async1() {console.log('async1 start');Promise.resolve(async2()).then(() => {console.log('async1 end')})
}
面試題
面試題1
async function async1() {console.log('1') // 2async2().then(() => {console.log('2')})
}
async function async2() {console.log('3') // 3
}
console.log('4') // 1
setTimeout(function () {console.log('5')
}, 0)
async1();
new Promise(function (resolve) {console.log('6') // 4resolve();
}).then(function () {console.log('7')
})
console.log('8') // 5
//4 1 3 6 8 2 7 5
面試題2
setTimeout(() => {console.log(1);}, 0);async function main1() {new Promise((resolve, reject) => {console.log(2);resolve();}).then(() => {console.log(3);})await main2();console.log(7);}function main2() {console.log(8);}main1();setTimeout(() => {console.log(10);}, 0);//
2
8
3
7
1
10
面試題3
Promise.resolve().then(() => {console.log(0);return Promise.resolve('4x');}).then((res) => { console.log(res) })
Promise.resolve().then(() => { console.log(1); }).then(() => { console.log(2); }, () => { console.log(2.1) }).then(() => { console.log(3); }).then(() => { console.log(5); }).then(() => { console.log(6); })// 0 1 2 3 4x 5 6
面試題4? 詳解:(1 封私信) 關于promise輸出順序的疑問? - 知乎 (zhihu.com)
new Promise((resolve,reject) => {console.log('外部promise')resolve()
})
.then(() => {console.log('外部第一個then')new Promise((resolve,reject) => {console.log('內部promise')resolve()}).then(() => {console.log('內部第一個then')return Promise.resolve()}).then(() => {console.log('內部第二個then')})
})
.then(() => {console.log('外部第二個then')
})
.then(() => {console.log('外部第三個then')
})
.then(() => {console.log('外部第四個then')
}) //
外部promise外部第一個then內部promise
內部第一個then
外部第二個then
外部第三個then外部第四個then內部第二個then
面試題5
// A 任務
setTimeout(() => {console.log(1)
}, 20)// B 任務
setTimeout(() => {console.log(2)
}, 0)// C 任務
setTimeout(() => {console.log(3)
}, 10)// D
setTimeout(() => {console.log(5)
}, 10)console.log(4)
/* 輸出
* 4 -> 2-> 3 -> 5 -> 1
*/
面試題6
setTimeout(function () {console.log(1)
}, 0);new Promise(function (resolve, reject) {console.log(2)for (var i = 0; i < 10000; i++) {if (i === 10) {console.log(10)}i == 9999 && resolve();}console.log(3)
}).then(function () {console.log(4)
})
console.log(5);
// 2, 10, 3, 5, 4, 1
面試題7
console.log("start");
setTimeout(() => {console.log("children2")Promise.resolve().then(() =>{console.log("children3")})
}, 0)new Promise(function(resolve, reject){console.log("children4")setTimeout(function(){console.log("children5")resolve("children6")}, 0)
}).then(res =>{ // flagconsole.log("children7")setTimeout(() =>{console.log(res)}, 0)
})
// start children4 children2 children3 children5 children7 children6
面試題8
這道題的難點在于是?promise2
還是?async1 end
?先輸出。從全局宏任務之上而下執行時?await async2()
?后面的代碼?console.log('async1 end')
?先進入?promise
?中的微任務隊列,最后.then()
?中的console.log('promise2')
?再進入到?promise
?中的微任務隊列。所以再開始下一輪宏任務循環之前先輸出了?async1 end
?再輸出了?promise2
。全局中的微任務執行完成開始下一輪宏任務setTimeout
?最后輸出?setTimeout
。
async function async1() {console.log('async1 start')await async2()console.log('async1 end')
}
async function async2() {console.log('async2')
}
console.log('script start')
setTimeout(function () {console.log('setTimeout')
}, 0)
async1()
new Promise((resolve) => {console.log('promise1')resolve()
}).then(function () {console.log('promise2')
})
console.log('script end')
//輸出
//script start
//async1 start
//async2
//promise1
//script end
//async1 end
//promise2
//setTimeout
面試題9?
首先開始全局下的宏任務依次輸出 script start, async1 start, promise1, promise3, script end。其中?await async2();
,async2()
?中.then()
的代碼先進入到promise
的微任務隊列,await async2();
?后面的代碼再進入到promise
的任務隊列,console.log('promise4');
?最后進入到?promise
?的任務隊列。全局下的宏任務結束,開始全局下的微任務,promise 的微任務隊列中按照隊列的先進先出原則依次輸出,promise2,async1 end,promise4。全局微任務結束,開始下一輪的宏任務setTimeout
,最終輸出?setTimeout
。
async function async1() {console.log('async1 start');await async2();console.log('async1 end');
}
async function async2() {new Promise(function (resolve) {console.log('promise1');resolve();}).then(function () {console.log('promise2');});
}
console.log('script start');
setTimeout(function () {console.log('setTimeout');
}, 0)
async1();
new Promise(function (resolve) {console.log('promise3');resolve();
}).then(function () {console.log('promise4');
});
console.log('script end');
//script start,
// async1 start,
// promise1,
// promise3,
// script end,
// promise2,
// async1 end,
// promise4,
// setTimeout
面試題10
async function async1() {console.log('async1 start');await async2();setTimeout(function() {console.log('setTimeout1') // 這一部分代碼會放入到 promise 的微任務隊列中。},0)
}
async function async2() {setTimeout(function() {console.log('setTimeout2')},0)
}
console.log('script start');
setTimeout(function() {console.log('setTimeout3');
}, 0)
async1();
new Promise(function(resolve) {console.log('promise1');resolve();
}).then(function() {console.log('promise2');
});
console.log('script end');
// script start, async1 start, promise1, script end,
// promise2, setTimeout3, setTimeout2, setTimeout1