JavaScript高级程序设计笔记 11
第11章 期约与异步函数 — 快速复习笔记
1. 异步编程背景
- JavaScript 是单线程、基于事件循环的异步模型。
- 传统异步方式:回调函数(Callback Hell / 金字塔问题)。
- ES6 引入 Promise(期约),ES2017 引入 async/await。
2. 期约(Promise)
2.1 期约基础
- 期约是一个对象,代表一个异步操作的最终完成(或失败)及其结果值。
- 状态(不可逆):
pending(待定):初始状态。fulfilled(兑现/解决):操作成功完成。rejected(拒绝):操作失败。
- 创建 Promise:
new Promise((resolve, reject) => { ... })。- 调用
resolve(value)将状态改为 fulfilled。 - 调用
reject(reason)将状态改为 rejected,或抛出错误也会触发 reject。
- 调用
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('done'), 1000);
});
2.2 实例方法
then(onFulfilled, onRejected):注册状态变为 fulfilled 或 rejected 时的回调。- 返回一个新的 Promise,实现链式调用。
catch(onRejected):语法糖,then(null, onRejected)。finally(onFinally):无论状态如何都会执行,一般用于清理操作。返回新 Promise,值会穿透。
promise
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('done'));
2.3 链式调用与值传递
- 每个
then/catch都返回新 Promise,支持链式异步任务。 - 回调函数的返回值会作为下一个
then的输入。 - 如果返回的是 Promise,它会等待其落定后再继续。
2.4 静态方法
| 方法 | 说明 |
|---|---|
Promise.resolve(value) |
将 value 转为 resolved promise(如果是 thenable 则会展开) |
Promise.reject(reason) |
返回 rejected promise |
Promise.all(iterable) |
全部成功才成功;任一失败则整体失败(拒绝值就是第一个失败原因) |
Promise.allSettled(iterable) |
等待所有 promise 落定,返回每个 promise 的状态和结果(ES2020) |
Promise.race(iterable) |
返回最先落定的 promise(无论成功或失败) |
Promise.any(iterable) |
返回第一个成功的 promise;全部失败才失败(ES2021) |
Promise.all([promise1, promise2])
.then(([res1, res2]) => console.log(res1, res2))
.catch(err => console.error(err));
2.5 错误处理与未捕获
- 未处理的 rejected promise 会在控制台报错(unhandledrejection 事件可监听)。
- 建议始终添加
catch或then的第二参数来捕获错误。
3. 异步函数(async/await)
3.1 async 关键字
- 在函数声明前加
async:async function foo() {}。 - 异步函数总是返回一个 Promise:返回值会被
Promise.resolve()包装;如果抛出错误,返回 rejected promise。
async function getData() {
return 42; // 等价于 Promise.resolve(42)
}
getData().then(console.log); // 42
3.2 await 关键字
- 只能在
async函数内部使用(顶层 await 在 ES2022 模块中允许)。 await后跟一个 Promise,会暂停函数执行,等待 Promise 落定后恢复。- 如果 Promise 成功,返回 resolved 的值;如果失败,抛出异常(可用 try-catch 捕获)。
async function fetchData() {
try {
let result = await fetch('/api');
let data = await result.json();
console.log(data);
} catch (err) {
console.error('请求失败', err);
}
}
3.3 与 Promise 的关系
async/await是基于 Promise 的语法糖,使异步代码写起来像同步代码。- 等价于
promise.then(...)的链式调用,但可读性更好。
3.4 错误处理
- 使用
try/catch捕获await的错误。 - 若不加
try/catch,错误会使得异步函数返回 rejected promise,可在外层用catch捕获。
3.5 并发执行
await会暂停函数,如果多个异步任务无依赖,应并发执行提升性能:
// 错误:顺序执行,总耗时 = 耗时1 + 耗时2
let a = await asyncTask1();
let b = await asyncTask2();
// 正确:并发执行
let [a, b] = await Promise.all([asyncTask1(), asyncTask2()]);
4. 常见模式与最佳实践
4.1 异步任务串行 vs 并行
- 串行:一个接一个执行(适合有依赖关系)。
- 并行:使用
Promise.all/Promise.allSettled。
4.2 超时控制
function timeoutPromise(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
return Promise.race([promise, timeout]);
}
4.3 节流/防抖(结合异步)
- 通常用标志位或取消机制避免重复请求。
5. 常见面试题速查
-
Promise 有哪几种状态?
→ pending、fulfilled、rejected,状态变化不可逆。 -
then 方法返回值是什么?
→ 返回一个新的 Promise,可实现链式调用。 -
如何让多个 Promise 并发执行并等待全部成功?
→Promise.all。 -
Promise.all与Promise.allSettled的区别?
→all一失败就 reject;allSettled等待所有完成,返回每个的状态。 -
Promise.race和Promise.any的区别?
→race返回最先落定的(不论成功/失败);any返回第一个成功的,全失败才 reject。 -
async/await 相比 Promise 的优势?
→ 语法更同步,可读性好,错误处理使用 try-catch 更自然。 -
如何在 async 函数中实现并发请求?
→ 使用Promise.all启动所有请求,再await。 -
顶层 await 是什么?
→ 在 ES2022 模块中,可以不在 async 函数内直接使用await,模块会等待该 Promise 完成。 -
如何取消一个 Promise?
→ Promise 本身不可取消,但可以使用 AbortController 配合 fetch 或其他方式实现可取消的异步任务。 -
未捕获的 rejected promise 会怎样?
→ 触发unhandledrejection事件,并在控制台打印错误。建议全局监听或始终添加 catch。

浙公网安备 33010602011771号