JS 是單線程語(yǔ)言。這句話對(duì)不對(duì)?
按照目前的情況來(lái)看,JS 自從支持了 Web Worker
之后,就不再是單線程語(yǔ)言了,但 Worker 的工作線程與主線程有區(qū)別,在 Worker 的工作線程中無(wú)法直接操作 DOM、window 對(duì)象或大多數(shù)瀏覽器 API(如 localStorage),Worker 的全局對(duì)象也不再是 window 對(duì)象,而是 self。
Worker 中的事件循環(huán)與主線程相互獨(dú)立,互不影響,但執(zhí)行順序還是得遵循 JS 的語(yǔ)法規(guī)則。
宏任務(wù)
宏任務(wù)表示執(zhí)行時(shí)間較長(zhǎng)的任務(wù),在每次時(shí)間循環(huán)時(shí)只會(huì)執(zhí)行一個(gè)宏任務(wù),執(zhí)行完畢后處理微任務(wù)隊(duì)列,所有微任務(wù)都執(zhí)行完畢后進(jìn)入下一個(gè)宏任務(wù)。
JS 常見宏任務(wù)類型:
- 定時(shí)器任務(wù):setTimeout / setInterval
- DOM 事件回調(diào)(如 click、scroll)
- I/O 操作(如文件讀取、網(wǎng)絡(luò)請(qǐng)求)
- 瀏覽器用于執(zhí)行動(dòng)畫的方法
requestAnimationFrame
,執(zhí)行時(shí)機(jī)與渲染相關(guān) - Node.js 環(huán)境的
setImmediate
- script 標(biāo)簽內(nèi)主線程的同步代碼(整體作為一個(gè)宏任務(wù))
微任務(wù)
微任務(wù)表示更輕量的異步任務(wù),當(dāng)宏任務(wù)執(zhí)行完畢之后立即執(zhí)行。
JS 常見微任務(wù)類型:
Promise.then()
/ Promise.catch()
/ Promise.finally()
- 瀏覽器監(jiān)聽 DOM 變化的 API 對(duì)象,比如:
MutationObserver
- 手動(dòng)添加微任務(wù)API方法:
queueMicrotask()
- nodejs 中的
process.nextTick()
代碼解析
看這么一段代碼:
(function() {
console.log(1)
setTimeout(() => { console.log(2); });
queueMicrotask(() => console.log(3))
new Promise(resolve => {
console.log(4);
setTimeout(() => {
resolve();
console.log(5);
}, 0);
Promise.resolve().then(() => console.log(6));
console.log(7);
}).then(() => {
console.log(8);
Promise.resolve().then(() => console.log(9));
});
console.log(10);
})();
分析代碼:
(function() {
console.log(1)
setTimeout(() => { console.log(2); });
queueMicrotask(() => console.log(3))
new Promise(resolve => {
console.log(4);
setTimeout(() => {
resolve();
console.log(5);
}, 0);
Promise.resolve().then(() => console.log(6));
console.log(7);
}).then(() => {
console.log(8);
Promise.resolve().then(() => console.log(9));
});
console.log(10);
})();
第一輪
首先同步代碼的宏任務(wù)優(yōu)先級(jí)最高,不管微任務(wù)還是宏任務(wù),同步代碼都會(huì)先執(zhí)行。
所以上面代碼會(huì)優(yōu)先執(zhí)行:
console.log(1)
console.log(4);
console.log(7);
console.log(10);
接著開始處理微任務(wù):
queueMicrotask(() => console.log(3))
Promise.resolve().then(() => console.log(6));
微任務(wù)處理完,開始執(zhí)行下一輪宏任務(wù)。
第二輪
這一輪中的宏任務(wù)只有一個(gè) setTimeout,執(zhí)行完之后由于沒(méi)有微任務(wù)隊(duì)列,所以直接執(zhí)行下一輪宏任務(wù)。
setTimeout(() => { console.log(2); });
第三輪
這一輪的宏任務(wù)中有同步代碼。
setTimeout(() => {
resolve();
console.log(5);
}, 0);
在執(zhí)行完 resolve()
之后,會(huì)將 Promise.then 的回調(diào)函數(shù)放入微任務(wù)隊(duì)列中,所以在宏任務(wù)執(zhí)行完之后會(huì)開始微任務(wù):
then(() => {
console.log(8);
Promise.resolve().then(() => console.log(9));
})
最終的打印順序
1
4
7
10
3
6
2
5
8
9
執(zhí)行流程圖
JS 代碼逐行執(zhí)行,在遇到宏任務(wù)時(shí),整個(gè)代碼塊丟到宏任務(wù)隊(duì)列,在遇到微任務(wù)時(shí),將微任務(wù)丟到本次事件循環(huán)中的微任務(wù)隊(duì)列,本次事件循環(huán)執(zhí)行完之后,再執(zhí)行微任務(wù)隊(duì)列中的任務(wù),微任務(wù)執(zhí)行完之后開始下一個(gè)宏任務(wù)執(zhí)行。
JS 代碼執(zhí)行機(jī)制:

宏任務(wù)執(zhí)行機(jī)制:

寫在最后
JS 中的代碼執(zhí)行流程永遠(yuǎn)都是事件循環(huán)機(jī)制,這是 JS 的任務(wù)調(diào)度核心,理解事件循環(huán)機(jī)制,才能在開發(fā)中游刃有余~~
?轉(zhuǎn)自https://www.cnblogs.com/linx/p/18943769
該文章在 2025/10/13 8:42:24 編輯過(guò)