不妨大膽一點斋攀,反正沒有誰能活著離開這世界
在前端的面試中,常常會考察async 梧田,await淳蔼,setTimeout,Promise函數(shù)執(zhí)行的順序裁眯。請各位看官鹉梨,細聽分說。以后不再迷茫
任務隊列
- JS分為同步任務和異步任務
- 同步任務都在主線程上(其實js是單線程)執(zhí)行穿稳,由上至下形成一個執(zhí)行棧
- 主線程之外存皂,事件觸發(fā)線程管理著一個任務隊列,來了異步任務有了運行結(jié)果逢艘,就在任務隊列里放置一個事件
- 一旦執(zhí)行棧里所有的同步任務執(zhí)行完畢的時候(及時JS引擎空閑)旦袋,系統(tǒng)就會讀取任務隊列骤菠,將可運行的異步任務添加到可執(zhí)行棧中,依次開始執(zhí)行
宏任務
macrotask疤孕,可以理解為每次執(zhí)行棧執(zhí)行的代碼就是一個宏任務娩怎。瀏覽器為了能夠使得JS內(nèi)部macrotask與DOM任務能夠有序的執(zhí)行,會在一個macrotask執(zhí)行結(jié)束后胰柑,在一個macrotask執(zhí)行開始前截亦,對頁面進行重新渲染
(macro)task->渲染->(macro)task->...
macrotask主要包含:script(整體代碼)、setTimeout柬讨、setInterval崩瓤、I/O、UI交互事件踩官、postMessage却桶、MessageChannel、setImmediate(Node.js 環(huán)境)
微任務
microtask蔗牡,可以理解是在當前task執(zhí)行結(jié)束后立即執(zhí)行的任務颖系。也就是說在當前任務之后,下一個任務之前辩越,重新渲染之前嘁扼。
所以microtask的響應速度比setTimeout(setTimeout是任務)更快,在某一個macrotask執(zhí)行完后黔攒,就會將在它執(zhí)行期間產(chǎn)生的所有microtask都執(zhí)行完畢(在渲染之前)
microtask主要包含:Promise.then趁啸、MutaionObserver、process.nextTick(Node.js 環(huán)境)
運行機制
事件循環(huán)中督惰,每進行一次循環(huán)操作稱為tick
- 執(zhí)行一個宏任務(執(zhí)行棧中沒有就從事件隊列中獲炔桓怠)
- 執(zhí)行過程中如果遇到微服務,就將它添加到微服務的隊列
- 宏任務執(zhí)行完畢后赏胚,立即執(zhí)行當前微服務隊列中的所有微服務(依次執(zhí)行)
-
渲染完畢之后,JS線程繼續(xù)接管访娶,開始下一個宏任務
image.png
Promise和async中的立即執(zhí)行
我們知道Promise中的異步體現(xiàn)在then和catch中,所以卸載Promise中的代碼是被當做同步任務立即執(zhí)行的觉阅。而在async/await中崖疤,在出現(xiàn)await之前,其中的代碼也是立即執(zhí)行的
await干了什么見不得人的事呢
await等待的是一個表達式留拾,這個表達式的返回值可以是一個promise對象也可以是其他值戳晌。由于因為async await 本身就是promise+generator的語法糖。所以await后面的代碼是microtask
做對下面的題痴柔,你就出師了
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(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
分析:
首先打印script start沦偎,然后遇到了宏任務setTimeout,宏任務會加入到宏任務的隊列
這時候開始執(zhí)行async1(),async1中await之間立即執(zhí)行豪嚎,打印async1 start搔驼,然后輸出async2,await之后的加入微觀任務
然后遇到promise侈询,promise里面的立即執(zhí)行舌涨。then分配到微觀任務
最后打印script end
這時候開始清空微任務'async1 end',promise2
最最后在循環(huán)執(zhí)行宏任務扔字,只剩下setTimeout囊嘉,打印setTimeout
答案如下:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
參考自:https://github.com/advanced-frontend/daily-interview-question/issues/7