單線程JavaScript為何如此高效
什么是js執(zhí)行機制
JavaScript 的執(zhí)行機制指的是 JavaScript 代碼在運行時的工作方式和順序。它涉及以下幾個關(guān)鍵概念:
- 單線程:JavaScript 是一門單線程的編程語言,意味著它只有一個主線程用于執(zhí)行代碼。這意味著 JavaScript 中的代碼是按順序執(zhí)行的,一次只能執(zhí)行一個任務(wù)。
- 任務(wù)隊列:JavaScript 通過任務(wù)隊列來管理要執(zhí)行的任務(wù)。任務(wù)隊列中存放著各種類型的任務(wù),包括同步任務(wù)和異步任務(wù)。
- 事件循環(huán):JavaScript 的事件循環(huán)是一個持續(xù)運行的過程,它負責監(jiān)視任務(wù)隊列并選擇下一個要執(zhí)行的任務(wù)。事件循環(huán)不斷地從任務(wù)隊列中獲取任務(wù)并將其交給主線程執(zhí)行。
- 同步任務(wù)和異步任務(wù):同步任務(wù)是按照順序在主線程上執(zhí)行的任務(wù),執(zhí)行一個任務(wù)時會阻塞后續(xù)任務(wù)的執(zhí)行。異步任務(wù)是在主線程上注冊并在將來某個時間點執(zhí)行的任務(wù),執(zhí)行異步任務(wù)時不會阻塞后續(xù)任務(wù)的執(zhí)行。
- 微任務(wù)和宏任務(wù):JavaScript 中的任務(wù)可以分為微任務(wù)和宏任務(wù)。微任務(wù)是在當前任務(wù)執(zhí)行完畢后立即執(zhí)行的任務(wù),它們使用微任務(wù)隊列進行管理。而宏任務(wù)是在事件循環(huán)的下一輪中執(zhí)行的任務(wù),它們使用任務(wù)隊列進行管理。
JavaScript 的執(zhí)行順序:
- 執(zhí)行同步任務(wù),將函數(shù)調(diào)用和變量分配到調(diào)用棧中按順序執(zhí)行。
- 遇到異步任務(wù),如定時器、事件監(jiān)聽等,將其注冊到任務(wù)隊列中,并繼續(xù)執(zhí)行后續(xù)的同步代碼。
- 當同步代碼執(zhí)行完畢后,主線程會檢查微任務(wù)隊列,依次執(zhí)行隊列中的微任務(wù)。
- 執(zhí)行完微任務(wù)后,主線程會從任務(wù)隊列中取出一個宏任務(wù)執(zhí)行。
- 循環(huán)執(zhí)行步驟 3 和步驟 4,直至任務(wù)隊列和微任務(wù)隊列都為空。
需要注意的是,JavaScript 中的異步任務(wù)通常是通過回調(diào)函數(shù)、Promise、async/await 等機制來處理。通過合理使用異步任務(wù)和任務(wù)隊列,可以實現(xiàn)非阻塞的代碼執(zhí)行,提高代碼的性能和響應(yīng)能力。JavaScript 的執(zhí)行機制主要涉及以下幾個概念:調(diào)用棧、事件循環(huán)和任務(wù)隊列。文字有點單調(diào),看看下面的圖理解理解
圖片
讓我們通過一個例子來解釋這些概念。假設(shè)我們有以下代碼:
console.log("Script start");
setTimeout(function() {
console.log("setTimeout");
}, 0);
Promise.resolve().then(function() {
console.log("Promise");
});
console.log("Script end");這段代碼的執(zhí)行機制如下:
- 首先,開始執(zhí)行代碼,遇到第一行 console.log("Script start"),它會立即打印 "Script start"。
- 接下來,遇到 setTimeout,它是一個異步函數(shù),會被放入任務(wù)隊列中,并設(shè)置一個定時器。由于定時器時間為 0,所以不會立即執(zhí)行。
- 然后,遇到 Promise.resolve().then(),它會創(chuàng)建一個 Promise 對象,并將 .then() 中的回調(diào)函數(shù)放入微任務(wù)隊列中。
- 繼續(xù)執(zhí)行下一行,打印 "Script end"。
- 此時,主線程上的同步代碼執(zhí)行完畢,開始執(zhí)行微任務(wù)隊列中的任務(wù)。首先執(zhí)行 Promise 的回調(diào)函數(shù),打印 "Promise"。
- 接著,主線程開始執(zhí)行任務(wù)隊列中的任務(wù)。由于定時器時間到達,setTimeout 的回調(diào)函數(shù)被放入任務(wù)隊列中。
- 最后,主線程執(zhí)行任務(wù)隊列中的任務(wù),打印 "setTimeout"。
綜上所述,JavaScript 的執(zhí)行機制遵循以下步驟:
- 執(zhí)行同步代碼,將函數(shù)調(diào)用和變量分配到調(diào)用棧中按順序執(zhí)行。
- 遇到異步操作,如定時器、事件監(jiān)聽等,將其注冊到任務(wù)隊列中,并繼續(xù)執(zhí)行后續(xù)的同步代碼。
- 同步代碼執(zhí)行完畢后,主線程會檢查微任務(wù)隊列,依次執(zhí)行隊列中的微任務(wù)(Promise 回調(diào)函數(shù))。
- 執(zhí)行完微任務(wù)后,主線程會從任務(wù)隊列中取出任務(wù)執(zhí)行,執(zhí)行完一個任務(wù)后再檢查微任務(wù)隊列,如此循環(huán),直至任務(wù)隊列為空。
需要注意的是,微任務(wù)優(yōu)先級高于任務(wù)隊列中的任務(wù),所以在執(zhí)行任務(wù)隊列中的任務(wù)之前,會先執(zhí)行完所有的微任務(wù)。
現(xiàn)學(xué)現(xiàn)用,再看一個例子:
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("js start");
setTimeout(function () {
console.log("timeout");
}, 0);
async1();
new Promise(function (resolve) {
console.log("promise");
resolve();
}).then(function () {
console.log("then");
});
console.log("js end");這段代碼的打印順序如下:
- "js start":立即打印,表示 JavaScript 代碼的開始執(zhí)行。
- "async1 start":由于 async1 函數(shù)被調(diào)用,所以會打印 "async1 start"。
- "async2":async1 函數(shù)中調(diào)用了 async2 函數(shù),因此會打印 "async2"。
- "promise":new Promise 的回調(diào)函數(shù)立即執(zhí)行,所以會打印 "promise"。
- "js end":立即打印,表示 JavaScript 代碼的執(zhí)行結(jié)束。
- "async1 end":由于 async2 函數(shù)是一個異步函數(shù),await async2() 表達式會等待 async2 函數(shù)執(zhí)行完畢,然后繼續(xù)執(zhí)行下面的代碼,所以會打印 "async1 end"。
- "then":Promise 的 then 方法是異步執(zhí)行的,所以會在下一個事件循環(huán)中執(zhí)行,因此會打印 "then"。
- "timeout":由于 setTimeout 的延遲時間為 0,所以會在下一個事件循環(huán)中執(zhí)行,因此會打印 "timeout"。代碼的執(zhí)行順序是按照同步代碼的順序執(zhí)行,異步代碼則根據(jù)事件循環(huán)的機制來執(zhí)行。async/await 會暫停同步代碼的執(zhí)行,并等待異步操作完成后再繼續(xù)執(zhí)行后續(xù)的代碼。
總結(jié)
JS 代碼的執(zhí)行順序主要為:
- 同步代碼 同步代碼(sync code)直接進入執(zhí)行棧執(zhí)行。執(zhí)行順序按代碼書寫順序。
- 異步任務(wù)回調(diào) 異步任務(wù)(如 setTimeout)進入任務(wù)隊列。
- 事件循環(huán) 事件循環(huán)周期性地從任務(wù)隊列取出任務(wù),推入執(zhí)行棧執(zhí)行。當執(zhí)行棧為空時,才會取出隊列中的任務(wù)。
- 執(zhí)行棧先進后出 執(zhí)行棧采用先進后出的方式執(zhí)行函數(shù)。在函數(shù)執(zhí)行完畢后才會執(zhí)行上層函數(shù)。這保證了函數(shù)的正確嵌套調(diào)用。
- 微任務(wù)優(yōu)先級高于宏任務(wù)
- 宏任務(wù)(macrotask):出于任務(wù)隊列的任務(wù)。比如 setTimeout、setInterval。
- 微任務(wù)(microtask):比如 Promise .then、MutationObserver 。微任務(wù)的優(yōu)先級高于宏任務(wù)。所以整個執(zhí)行順序可以描述為:
- 同步代碼按順序進入執(zhí)行棧執(zhí)行
- 異步宏任務(wù)進入任務(wù)隊列
- 當執(zhí)行棧清空時,執(zhí)行微任務(wù)
- 接著執(zhí)行宏任務(wù)
- 循環(huán)往復(fù)
























