文章目录
- 一、先抛一个生活例子:奶茶店的接单流程
- 二、JS事件循环的核心概念(人话版)
- 三、事件循环的执行流程(带代码例子)
- 1.
基础代码示例
- 2.
执行过程拆解(跟着店员的节奏走)
- 3.
进阶:宏任务
微任务(优先级别)
- 代码示例+执行顺序
- 1.
- 四、一张图总结事件循环流程
- 五、新手常见误区
- 六、实战小练习(检验是否理解)
- 总结
一、先抛一个生活例子:奶茶店的接单流程
先不聊代码,咱们先看个日常场景:
/>你去奶茶店点单,店员只有1个(对应JS的单线程),所有订单都得排着来:
- 你点了一杯珍珠奶茶(同步任务):店员立刻接单、配原料、做奶茶,做完才能接下一个单;
- 下一个顾客点了现煮的芋泥奶茶,需要等10分钟(异步任务):店员不会傻等,会先记下这个订单,给顾客一个取餐号,然后继续接下一个简单的订单(比如打包现成的柠檬水);
- 等芋泥煮好后(异步任务完成),店员会喊取餐号,优先处理这个订单(回调执行)。
JS的事件循环,本质就是这个“接单-处理-回调”的流程,核心解决的是“单线程如何处理耗时任务”的问题。
二、JS事件循环的核心概念(人话版)
先把几个关键术语翻译成大白话,避免一上来就懵:
| 专业术语 | 人话解释 | 对应奶茶店场景 |
|---|---|---|
| 单线程 | JS只有一个“执行线程”,同一时间只能做一件事 | 奶茶店只有1个店员 |
| 同步任务 | 立刻能做完、不耗时的任务 | 做现成的柠檬水、打包奶茶 |
| 异步任务 | 耗时的任务(比如网络请求、定时器、DOM事件) | 煮芋泥、等外卖小哥取餐 |
| 调用栈 | 存放正在执行的同步任务,“先进后出” | 店员手里正在处理的订单清单 |
| 任务队列 | 存放等待执行的异步回调任务 | 奶茶店的待取餐订单列表 |
| 事件循环 | 持续检查“调用栈是否为空”,空了就从任务队列取任务执行 | 店员不停看手里的活干完没,干完就去待取餐列表拿订单 |
三、事件循环的执行流程(带代码例子)
咱们把奶茶店的流程对应到代码里,一步一步看执行过程:
1.
同步任务:立刻执行console.log('1.
点单成功');//
异步任务:定时器(耗时任务)setTimeout(()=>{console.log('2.
芋泥奶茶做好了');},0);//
同步任务:立刻执行console.log('3.
打包柠檬水完成');
2.
执行过程拆解(跟着店员的节奏走)
第一步:JS引擎(店员)先执行调用栈里的同步任务
- 执行
console.log('1.→点单成功')
点单成功
- 遇到
setTimeout:这是异步任务,JS引擎不等待,直接把回调函数(())丢到“任务队列”里,继续执行下一个同步任务=>
芋泥奶茶做好了')}
- 执行
console.log('3.输出:3.
打包柠檬水完成
第二步:调用栈空了,事件循环开始工作
- 事件循环检查到“调用栈为空”,就去任务队列里取第一个回调任务,放到调用栈执行
- 执行
console.log('2.输出:2.
芋泥奶茶做好了
最终输出顺序:
1.点单成功
微任务(优先级别)
刚才的例子是“宏任务”,实际还有优先级更高的“微任务”,咱们继续用奶茶店举例:
- 宏任务:普通异步任务(定时器、DOM事件、网络请求、script整体代码)→
对应“普通待取餐订单”
- 微任务:高优先级异步任务(Promise.then/catch/finally、async/await、queueMicrotask)→
对应“外卖订单(优先处理)”
核心规则:事件循环每次处理完调用栈的同步任务后,会先把所有微任务执行完,再执行一个宏任务。
代码示例+执行顺序
console.log('1.点单成功'
);//同步任务
//宏任务
setTimeout(()=>{console.log('2.普通订单:芋泥奶茶做好了'
);},0);//微任务
Promise.resolve().then(()=>{console.log('3.外卖订单:芝士奶盖做好了'
);});console.log('4.打包柠檬水完成'
);//同步任务
执行结果:
1.点单成功
普通订单:芋泥奶茶做好了
拆解:
- 先执行同步任务:输出
4
- 调用栈空了,先执行所有微任务:输出
3
- 微任务执行完,再执行宏任务:输出
2
四、一张图总结事件循环流程
style="display:
center;">
transform="translate(168.01557,
206.87545)">center;">否
center;"> 否
427)">center;">是
center;"> 是


