宏任务与微任务

一、事件循环(Event Loop)

1.1 事件循环是什么?

Event Loop 是一个死循环:它反复检查”调用栈是不是空了”,空了就从任务队列里取一个任务塞回调用栈执行。就这么周而复始,直到天荒地老。


graph TD

  subgraph EventLoop[Event Loop 灵魂伪代码]

    Loop["while true<br/>判断: 调用栈is空?"] -->|是| Get["task = 任务队列.取出一个<br/>队列先进先出"]

    Get -->|task存在| Push["调用栈.推入task<br/>拿出来执行"]

    Push --> Loop

    Get -->|task不存在| Loop

  end

注意:这是简化版真正的循环还要分宏/微任务,先这样理解,下面第六节讲完整流程。

1.2 四个组成部分


graph TD

  CS[① 调用栈 Call Stack<br/>JS引擎执行同步代码的地方<br/>一个执行完弹出<br/>下一个才能进]

  CS -->|异步操作交给| APIs[② Web APIs / Node APIs<br/>浏览器/Node提供的慢操作处理处<br/>setTimeout计时 网络请求发送等]

  APIs -->|时机到了 把回调推进队列| TQ[③ 任务队列 Task Queue<br/>存放准备好了待执行的回调]

  TQ -->|Event Loop取出| EL[④ Event Loop 事件循环<br/>盯着调用栈<br/>空了就从队列拿一个进去执行]

1.3 用一道题模拟 Event Loop 的脑子

 
console.log('1');                       // ① 同步,立即输出
 
setTimeout(function() {
 
    console.log('2');                   // ④ 异步,进队列,最后输出
 
}, 0);
 
console.log('3');                       // ② 同步,立即输出
 

模拟引擎大脑,逐时刻拆:


sequenceDiagram

  participant CS as 调用栈

  participant API as Web API

  participant Q as 任务队列

  participant Screen as 屏幕

  Note over CS,Screen: 时刻1

  CS->>CS: console.log'1'进栈 → 执行

  CS->>Screen: 输出 '1'

  CS->>CS: 弹出

  Note over CS,Screen: 时刻2

  CS->>CS: setTimeout进栈

  CS->>API: 帮我计时0ms<br/>到点回调放回队列

  API->>CS: setTimeout本身立刻弹出

  Note over Screen: 屏幕: 1

  Note over CS,Screen: 时刻3

  CS->>CS: console.log'3'进栈 → 执行

  CS->>Screen: 输出 '3'

  CS->>CS: 弹出

  Note over Screen: 屏幕: 1 3

  Note over CS,Screen: 时刻4: 调用栈空了!

  CS->>Q: Event Loop发现队列有回调

  Q->>CS: 推进调用栈

  CS->>CS: 执行 → 输出'2'

  CS->>Screen: 输出 '2'

  CS->>CS: 弹出

  Note over Screen: 最终: 1 3 2

关键认知setTimeout(fn, 0) 的 0 不是”立刻执行”,而是”至少等 0 毫秒”,真正的执行时机是”调用栈空了之后”。这是所有异步题的解题钥匙。


速记卡(面试闪卡)

Q1:一句话讲清「宏任务与微任务」到底是什么?

A:事件循环是个死循环:调用栈空了就从任务队列取一个任务执行,setTimeout 的 0 不是立刻跑而是栈空后才跑。

Q2:一、Event Loop 是什么 —— 怎么理解? —— 怎么理解?

A:Event Loop 反复检查”调用栈是不是空了”,空了就从任务队列取一个任务塞回调用栈执行,周而复始。就像餐厅服务员:手头活干完,才去叫下一个号。它由调用栈、Web API、任务队列四部分组成。

Q3:二、四个组成部分 —— 怎么理解? —— 怎么理解?

A:①调用栈:JS 引擎执行同步代码,一个进一个出;②Web API:浏览器接管慢操作(计时、网络);③任务队列:准备好的回调排队;④Event Loop:盯着调用栈,空了就从队列取一个。异步的本质是”慢活外包给 API,好了再回队列”。

Q4:三、一道题模拟引擎脑子 —— 怎么理解? —— 怎么理解?

A:console.log('1'); setTimeout(()=>log('2'),0); console.log('3') 输出 1 3 2。因为 setTimeout 交给 Web API 计时,同步的 1 和 3 先跑完、栈空了,Event Loop 才把回调推进栈执行 2。关键认知:0 不是立刻,是”至少等 0ms、栈空后”。

Q5:四、宏任务与微任务的完整流程(预告)—— 怎么理解? —— 怎么理解?

A:上面是简化版——真正的循环还要分宏任务(macrotask,如 setTimeout、IO)和微任务(microtask,如 Promise.then)。每轮先执行一个宏任务,再清空所有微任务,才取下一个宏任务。这是所有异步排序题的底层规则(笔记第六节展开)。

Q6:核心速记主线有哪些?

  • Event Loop:栈空了就从队列取任务执行

  • 四部件:调用栈 / Web API / 任务队列 / Event Loop

  • setTimeout(fn,0) 的 0 是”栈空后”,不是立刻

  • 真循环分宏任务与微任务:先宏后清微,再下一个宏

口诀

A:事件循环死循环,

栈空才把任务搬;

setTimeout 零不是马上,

宏微分清不乱窜。

相关链接