宏任务与微任务
一、事件循环(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 零不是马上,
宏微分清不乱窜。
相关链接
-
📋 目录:00-JavaScript
-
📚 学习清单:八股文学习路线图