外观
async 和 await
约 1189 字大约 4 分钟
2025-09-27
我们通常使用 async 和 await 来简化异步编程,特别是避免回调地狱和 Promise 链的深层嵌套。
简介
async
- 标记异步函数:放在函数声明前,表明该函数是异步的。
- 返回 Promise:
async
函数总是返回一个 Promise 对象。如果函数正常有响应,该值会被自动包装成Promise.resolve(返回值)
;如果函数抛出错误,则返回Promise.reject(错误)
。
await
- 等待 Promise:
await
后面通常跟一个 Promise 对象,它会暂停async
函数的执行,直到 Promise 完成(resolve 或 reject)。 - 提取结果:如果 Promise 兑现,
await
返回兑现的值;如果 Promise 拒绝,则抛出拒绝的原因(错误),可以使用try/catch
捕获。 - 暂停与恢复:
await
会暂停async
函数的执行,但不会阻塞整个程序(因为它是非阻塞的异步操作)。当 Promise 完成后,async
函数从暂停处恢复执行。
主要作用
- 简化异步代码:让异步代码的写法更像同步代码,提高可读性。
- 错误处理:可以使用
try/catch
来捕获异步操作中的错误,与同步代码的错误处理方式一致。 - 避免回调地狱:不再需要多层嵌套的回调函数或长长的 Promise 链。
常用场景
- 处理异步请求
// 等待接口请求完成
async function fetchWithCatch() {
const response = await fetch("/api/data").catch((error) => {
console.error("网络错误:", error);
throw error;
});
return response.json();
}
- 并行处理多个异步请求
// 并行处理多个异步请求
async function fastExample() {
const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);
return { user, posts };
}
实现原理
async / await
的本质,其实就是 Generator + Promise 的语法糖,它的实现原理:使用 yield
暂停执行,通过 next()
恢复执行,形成可中断的函数状态机。
// Generator 基础版本
function* generatorVersion() {
const user = yield fetchUser();
const posts = yield fetchPosts();
return { user, posts };
}
// async/await 版本
async function asyncVersion() {
const user = await fetchUser();
const posts = await fetchPosts();
return { user, posts };
}
2.2 Babel 编译结果分析
源代码:
async function example() {
const a = await 1;
const b = (await a) + 1;
return b;
}
编译后的近似代码:
function example() {
return _asyncToGenerator(function* () {
const a = yield 1; // 第一个 await
const b = yield a + 1; // 第二个 await
return b;
})();
}
2.3 核心自动执行器实现
function _asyncToGenerator(generatorFunc) {
return function () {
const generator = generatorFunc.apply(this, arguments);
return new Promise((resolve, reject) => {
function step(key, arg) {
let result;
try {
result = generator[key](arg);
} catch (error) {
return reject(error);
}
const { value, done } = result;
if (done) {
// Generator 执行完成
return resolve(value);
} else {
// 核心:将值包装为 Promise,然后继续执行
return Promise.resolve(value).then(
function onFulfilled(value) {
step("next", value); // 成功,继续下一步
},
function onRejected(error) {
step("throw", error); // 失败,抛出错误
}
);
}
}
step("next"); // 启动 Generator
});
};
}
2.4 状态机视角
async 函数被编译成状态机:
function _example() {
let state = 0;
let a, b;
return new Promise((resolve, reject) => {
function handleResult(result) {
switch (state) {
case 0: // 开始执行
state = 1;
return Promise.resolve(1);
case 1: // 第一个 await 完成
a = result;
state = 2;
return Promise.resolve(a + 1);
case 2: // 第二个 await 完成
b = result;
state = 3;
resolve(b); // 完成
break;
}
}
// 启动状态机
handleResult();
});
}
三、执行机制详解
3.1 微任务调度
async function microtaskDemo() {
console.log("1. 同步代码");
await Promise.resolve();
console.log("3. 微任务中的代码");
console.log("4. 继续执行");
}
console.log("0. 开始");
microtaskDemo();
console.log("2. 同步代码结束");
// 输出顺序:
// 0. 开始
// 1. 同步代码
// 2. 同步代码结束
// 3. 微任务中的代码
// 4. 继续执行
3.2 错误传播机制
async function errorFlow() {
try {
await Promise.reject("错误1");
} catch (err) {
console.log("捕获:", err); // 捕获: 错误1
throw "错误2"; // 重新抛出
}
}
errorFlow().catch((err) => console.log("最终捕获:", err)); // 最终捕获: 错误2
四、高级用法与模式
4.1 重试机制
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
if (i === retries - 1) throw error;
await sleep(1000 * (i + 1)); // 指数退避
}
}
}
4.2 超时控制
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return response.json();
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}
4.3 异步迭代
async function processItems(items) {
for (const item of items) {
// 顺序处理每个项目
const result = await processItem(item);
console.log(result);
}
}
// 或者使用 for-await-of
async function processStream(stream) {
for await (const chunk of stream) {
await processChunk(chunk);
}
}
六、总结
核心要点:
- 语法糖本质:基于 Generator + 自动执行器
- Promise 基础:所有 async 函数都返回 Promise
- 微任务调度:await 之后的代码进入微任务队列
- 错误冒泡:天然支持 try/catch 错误处理
最佳实践:
- 使用 try/catch 处理错误
- 并行化独立异步操作
- 避免过度使用 await
- 合理处理资源清理
async/await 让异步代码拥有同步代码的可读性,同时保持异步的非阻塞特性,是现代 JavaScript 异步编程的核心工具。
更新日志
2025/9/27 17:16
查看所有更新日志
1eb9b
-docs(Javascript): 更新Generator生成器函数文档内容于