03async 与 await
async/await 是 Promise 的「同步写法」糖:
用 await 等结果,用 try/catch 接错误,代码像同步一样从上往下读。
它不改变异步本质——底层仍是 Promise + 微任务。
一、从 then 链到 async 函数
Promise 链横向缩进
load() .then(d => parse(d)) .then(p => save(p)) .then(() => done()) .catch(e => fail(e));
async/await纵向阅读
try {
const d = await load();
const p = await parse(d);
await save(p);
done();
} catch (e) {
fail(e);
}
两种写法等价。新代码优先 async/await;
并发场景记得
Promise.all,别傻等串行。二、async 函数的返回值
async 函数永远返回 Promise:
直接 return 的值会被包成 Promise.resolve(值);
抛错则变成 rejected。
async 返回值自动包成 Promise:点按钮看 typeof 与 then 的表现
点按钮
三、await 在等什么
await 只能写在 async 函数(或模块顶层)里。
它等一个 Promise settle,把 fulfilled 的 value 交出来;
若 rejected,则抛出错误(可用 try/catch 接)。
四、错误处理:try / catch / finally
try/catch 接 await 失败:模拟网络错误,看错误被拦住而不是炸掉整页
点按钮
忘记
try/catch 的 await 失败会变成
unhandledrejection。在事件回调里调用 async 函数时,
也要保证内部有 catch,或 fn().catch(handle)。五、串行 vs 并行
连续 await 是串行(等前一个完成才发下一个)。
互不依赖的请求要用 Promise.all 并行,速度差可能是一倍以上。
计时对比:串行 3×200ms vs 并行 ≈200ms
点按钮测时间
口诀:依赖关系用串行,独立请求用 all。
六、常见模式
| 模式 | 写法 |
|---|---|
| 循环里串行 | for (const x of list) await f(x) |
| 循环里并行 | await Promise.all(list.map(f)) |
| 超时 | Promise.race([task(), wait(2000).then(() => { throw … })]) |
| 重试 | for (…) { try { return await f() } catch { if (last) throw } } |
| 事件回调 | btn.addEventListener('click', async () => { try { … } catch {} }) |
七、async 不是「多线程」
- await 只是让出主线程,等结果期间别的任务可以跑。
- 计算密集型任务 await 也救不了——用 Web Worker。
- async 函数里 await 之后的代码是微任务继续跑的。
八、本章小结
async函数永远返回 Promise;await等 Promise settle。- 错误用 try/catch 接;finally 做清理。
- 连续 await 是串行;独立请求用
Promise.all并行。 - 语法是糖,底层仍是 Promise 微任务——和事件循环知识连起来记。
- 项目里新写异步逻辑优先 async/await,读起来像同步。
动手练习:
① 把上节课的 Promise 链改写成 async/await;
② 写一个
retry(fn, times) 异步重试工具;
③ 用 Promise.all 并发取 3 个数据,失败的用 allSettled 跳过。