? 寫在前面:你不是不懂,是沒真正吃透
事件循環 幾乎是前端人繞不過的坎,但也總是似懂非懂。
你可能已經:
- 看了 5 張不同的流程圖
- 刷了 3 遍教學視頻
- 背過“宏任務和微任務”規則
但真正遇到面試題,比如:
Promise.then
和 setTimeout
誰先執行? async/await
究竟屬于哪類任務? process.nextTick
和 queueMicrotask
誰優先?
腦子還是“嗡”的一下。
這篇文章就是為了讓你 真正理解,而不是記答案。
?? 一句話解釋事件循環:JS 是單線程,但支持異步
JavaScript 是 單線程運行時:一次只能執行一個任務。
但我們又能寫異步邏輯:比如定時器、網絡請求、用戶操作。JS 怎么做到“非阻塞”呢?
答案就是:事件循環機制(Event Loop)
那它是怎樣執行的呢?
JS 引擎從任務隊列中不斷地調度任務 —— 執行一個宏任務 → 清空微任務 → 繼續下一輪宏任務。
- 主線程:JS 的唯一運行通道,只能同時跑一個任務。
- 執行棧:任務正在執行時,代碼的“調用路徑”。
- 任務隊列:未來要執行的代碼排隊等待。
主線程中,存在:
- 同步任務:立即執行的代碼(如
console.log()
) - 異步任務:不會立即執行,注冊后進入 任務隊列 等待,由瀏覽器安排時間再執行(如
setTimeout()
)
所有異步任務都要依賴事件循環調度!
一圖看懂 JS 執行順序:

那么哪些是宏任務、哪些又是微任務呢?
?? 宏任務 vs 微任務
類型 | 常見任務來源 | 什么時候執行 |
---|
宏任務 | setTimeout 、setInterval 、UI 渲染 、MessageChannel | 一輪事件循環的起點 |
微任務 | Promise.then 、queueMicrotask 、MutationObserver | 當前宏任務執行完立即執行 |
記住:一個宏任務執行完 → 清空所有微任務 → 執行下一個宏任務
?? 那 Web Worker 屬于什么任務?
直接給結論,Web Worker 屬于獨立線程運行,不占用主線程時間。
但是需要注意:
Worker發回的消息會被主線程當作 普通的宏任務 處理(和setTimeout
同級)
console.log("1. 開始炒菜");
const worker = new Worker("worker.js");
worker.postMessage("切土豆絲");
worker.onmessage = (e) => {
console.log("4. 收到切好的土豆絲");
};
console.log("2. 炒菜中...");
setTimeout(() => { console.log("3. 定時器任務"); }, 0);
self.onmessage = (e) => {
for (let i=0; i<1e9; i++) {}
self.postMessage("切好了");
};
輸出順序: 1 → 2 → 3 → 4(Worker計算耗時,但主線程不等待,先執行其他宏任務)
?? 舉幾個栗子徹底理解輸出順序
1. 來個簡單的練練手
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
輸出順序: 1 → 4 → 3 → 2
執行過程:
- 執行同步任務:
console.log(1)
- 注冊
setTimeout
(宏任務),加入任務隊列 Promise.then()
加入微任務隊列- 執行同步任務:
console.log(4)
- 清空微任務:輸出
3
- 下一輪宏任務:輸出
2
2. async/await 屬于什么任務?
async function test() {
console.log('a');
await Promise.resolve();
console.log('b');
}
test();
console.log('c');
輸出順序: a → c → b
拆解說明:
await
會將 console.log('b')
拆成一個微任務- 所以整個
async
函數執行到 await
暫停,主線程繼續跑 console.log('c')
- 主線程執行完后 → 微任務觸發 → 輸出
b
3. 來道高頻面試題試試手
先不看答案自己嘗試解答
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve()
.then(() => {
console.log('promise1');
return Promise.resolve();
})
.then(() => {
console.log('promise2');
});
queueMicrotask(() => {
console.log('queueMicrotask');
});
console.log('script end');
拆解過程:
- 先執行同步任務
console.log('script start')
- 注冊
setTimeout
(宏任務),加入任務隊列 Promise.then()
加入微任務隊列(promise1
)promise2
屬于 promise1
微任務執行完成后才執行,所以先觀察下面還有沒有別的微任務queueMicrotask
加入微任務隊列- 執行同步任務
console.log('script end')
執行結果:
script start
script end
promise1
queueMicrotask
promise2
setTimeout
你有沒有答對呢...
?? 面試怎么答才顯得專業?
? 面試官:“你能解釋下 JS 的事件循環嗎?”
你可以這樣答:
- 事件循環是 JS 控制異步執行順序的機制。
- 所有任務分為同步任務和異步任務。異步任務進一步劃分為宏任務和微任務。
- JS 每一輪事件循環會執行一個宏任務,執行完后清空所有微任務。
- 微任務優先于下一輪宏任務執行,所以 Promise.then 總是先于 setTimeout。
- async/await 本質上是 Promise 的語法糖,會拆成兩個微任務。
下次在面試中再被問到就應該 自信吟唱 了。??
?? 小結:寫給自己復習用
知識點 | 要點 |
---|
事件循環機制 | 宏任務 → 微任務 → 下一輪宏任務 |
同步任務 | 立即執行的代碼 |
異步任務 | 注冊進隊列等待(分宏/微任務) |
微任務 | Promise.then 、queueMicrotask ,總是在宏任務后立即執行 |
async/await | 會被拆成 .then() 形成的微任務 |
Web Worker | 提供多線程能力,但與主線程隔離,靠 postMessage 通信 |
面試答法 | 清晰區分同步/異步 + 宏/微任務 + 舉例輸出順序 |
轉自https://juejin.cn/post/7501880110837907508
該文章在 2025/5/13 14:09:05 編輯過