前言
瀏覽器執(zhí)行環(huán)境的核心思想基于:同一時刻只能執(zhí)行一個代碼片 段跷敬,即所謂的單線程執(zhí)行模型麻车。想象一下在銀行柜臺前排隊,每個人進 入一支隊伍等待叫號并“處理”受裹。但JavaScript則只開啟了一個營業(yè)柜臺碌补! 每當輪到某個顧客時(某個事件),只能處理該位顧客棉饶。
來看下面這段 JavaScript 代碼:
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
先猜測一下這段代碼的輸出順序是什么?
事件循環(huán)和任務隊列
所有的任務可以分為同步任務和異步任務厦章, 分別進入不同的執(zhí)行環(huán)境。
同步任務照藻,一般會直接進入到主線程中袜啃,立即執(zhí)行;而異步任務幸缕,就是異步執(zhí)行的任務(比如ajax網絡請求群发,setTimeout 定時函數等都屬于異步任務,異步任務會通過任務隊列( Event Queue )的機制來進行協(xié)調发乔。
當主線程內的任務執(zhí)行完畢熟妓,會去 任務隊列(Event Queue) 讀取對應的任務,推入主線程執(zhí)行栏尚。 上述過程的不斷重復就是我們說的 事件循環(huán)(Event Loop)起愈。
事件循環(huán)(Event Loop)通常至少需要兩個任務隊列:宏任務隊列和微任務隊列。兩種隊列在同一時 刻都只執(zhí)行一個任務译仗。
代碼分析
如上所說抬虽, 讓我們來看看上段代碼的執(zhí)行過程:
1.整體 script 作為第一個宏任務進入主線程,遇到 console.log纵菌,輸出 script start
2.遇到 setTimeout阐污,其回調函數被分發(fā)到宏任務 Event Queue 中
3.遇到 Promise,其 then函數被分到到微任務 Event Queue 中,記為 then1咱圆,之后又遇到了 then 函數笛辟,將其分到微任務 Event Queue 中功氨,記為 then2
4.遇到 console.log,輸出 script end
至此隘膘,Event Queue 中存在三個任務:
1.執(zhí)行微任務:執(zhí)行then1,輸出 promise1,
2.執(zhí)行微任務:執(zhí)行 then2杠览,輸出 promise2弯菊,
這樣就清空了所有微任務
3.執(zhí)行宏任務: 輸出 setTimeout
至此,輸出的順序是:
script start, script end, promise1, promise2, setTimeout
附上一題踱阿,你可以根據上述方法來做個練習:
console.log('script start');
setTimeout(function() {
console.log('timeout1');
}, 10);
new Promise(resolve => {
console.log('promise1');
resolve();
setTimeout(() => console.log('timeout2'), 10);
}).then(function() {
console.log('then1')
})
console.log('script end');
總結
記住管钳,JavaScript 是一門單線程語言,異步操作都是放到事件循環(huán)隊列里面软舌,等待主執(zhí)行棧來執(zhí)行的才漆。
參考文獻
Tasks, microtasks, queues and schedules
JavaScript忍者秘籍第二版 第十三章 彌久歷新的事件