Eson Wong's Blog

编程笔记,投资记录, 读书总结, 生活心得

0%

前端面试题解析之 Promise 和 setTimeout 执行顺序

微任务和宏任务的执行顺序是面试中经常会被问到的问题,这里就来解析一下。

下面的代码中,打印的顺序是什么呢?

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 (浏览器环境)

补充说明

  1. 宏任务 Macrotask 不是标准的术语, HTML Standard 中的定义是:Task。

  2. 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

欢迎关注我的其它发布渠道