异步的价值是,把耗时任务交给浏览器/系统处理,主线程先继续往下执行,等结果回来再处理。
- 异步 != 并行(parallel)
- 异步 != 并发(concurrency)
- 但异步是一种实现并发的手段之一,尤其是在前端里
任务可以分为两种:
| 类型 | 本质行为 |
|---|---|
| CPU 密集 | 一直在算,占着 CPU 不放 |
| IO 密集 | 在等,等待磁盘、网络、用户或数据 |
也就是说,异步解决的是单线程里带来的阻塞问题。
- 对 IO 任务:等待时间可以让主线程继续执行,通常会调用浏览器辅助线程,执行任务并注册回调
- 对 CPU 密集任务:本质上只是切换顺序,例如
setTimeout(() => { heavyCalc() })
异步也就是利用任务队列,对任务进行调度,其步调本来就不一致。
任务队列的调度机制就是事件循环。触发调度的本质原因只有两个:
- “等”(Wait):等 IO 结果、等时间流逝、等硬件信号。为了不让主线程死等,必须异步。
- “让”(Yield):计算量太大,主线程太累了。为了让用户交互不卡顿,必须把 CPU 任务拆散,通过任务队列重新排队,实现逻辑上的“并发”。
IO 链路
- JS 主线程负责执行代码
- IO 交给浏览器或宿主环境处理
- 事件循环负责把“结果”送回主线程执行
CPU 链路
- JS 主线程负责执行代码
- 耗时任务放到任务队列里延后处理
- 事件循环负责按顺序调用
外部系统 -> 产生回调
↓
进入任务队列
↓
事件循环调度执行
↓
表现为“顺序被改变”