微任务和宏任务的执行顺序是面试中经常会被问到的问题,这里就来解析一下。
下面的代码中,打印的顺序是什么呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| console.log("script 1");
setTimeout(() => console.log("timeout 1"));
Promise.resolve().then(() => console.log("promise 1"));
Promise.resolve().then(() => setTimeout(() => console.log("timeout in promise")) );
Promise.resolve().then(() => console.log("promise 2"));
setTimeout(() => console.log("timeout 2"));
console.log("script 2");
|
答案是:
1 2 3 4 5 6 7
| [Log] script 1 [Log] script 2 [Log] promise 1 [Log] promise 2 [Log] timeout 1 [Log] timeout 2 [Log] timeout in promise
|
解析
JavaScript 是单线程的,它的任务调度是基于事件循环的。事件循环中有两种任务队列:微任务队列和宏任务队列。
timeout 中的回调函数会被放到宏任务队列中,而 promise 中的回调函数会被放到微任务队列中。
上面代码的的执行顺序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| stateDiagram-v2
MTQ: 宏任务队列 Script: script 宏任务 note right of Script: 整个 script 作为一个宏任务 S1: console.log("script 1") T1: setTimeout() P1: Promise.resolve().then() P2: Promise.resolve().then() T2: setTimeout() S2: console.log("script 2")
MicrotaskQ:微任务队列 PL1: () => console.log("promise 1") PT: () => setTimeout(() => console.log("timeout in promise")) PL2: () => console.log("promise 2") PTL: () => console.log("timeout in promise") SL1: () => console.log("timeout 1") SL2: () => console.log("timeout 2")
state MTQ { [*] --> Script PL2 --> SL1 SL1 --> SL2 SL2 --> PTL }
state Script { [*] --> S1 S1 --> T1 T1 --> P1 note right of T1: 安排 () => console.log("timeout 1") 到宏任务队列 P1 --> P2 note right of P1: 安排 () => console.log("promise 1") 到微任务队列 P2 --> T2 note right of P2: 安排 () => console.log("promise 2") 到微任务队列 T2 --> S2 note right of T2: 安排 () => console.log("timeout 2") 到宏任务队列 }
S2 --> MicrotaskQ
state MicrotaskQ { [*] --> PL1 PL1 --> PT PT --> PL2 note left of PT: 安排 () => console.log("timeout in promise") 到宏任务队列 }
|
宏任务有哪些
- script
- setTimeout
- setInterval
- setImmediate
- requestIdleCallback
微任务有哪些
- Promise 的 then 和 catch
- process.nextTick (Node.js 环境)
- MutationObserver (浏览器环境)
补充说明
宏任务 Macrotask 不是标准的术语, HTML Standard 中的定义是:Task。
Task 和 Macrotask 、 UI rendering 的执行顺序
1 2 3 4 5 6 7 8 9 10 11
| flowchart LR
subgraph Event Loop Task --> Macrotask1 subgraph Macrotask Queue Macrotask1 --> Macrotask2 end Macrotask2 --> A["requestAnimationFrame() 的回调"] A --> UR[渲染 UI] UR --> Task end
|