async/await 是现代 JavaScript 中最优雅的异步编程方式。它让异步代码看起来像同步代码,极大提升了可读性和可维护性。
本文将带你彻底理解 async/await 的本质、工作原理和最佳实践。
一、async/await 是什么?
✅ 一句话定义
async/await是基于Promise的语法糖,它让异步代码写起来像同步代码一样直观。
async:声明一个函数是异步的;await:等待一个Promise完成,暂停函数执行,直到结果返回。
二、async 函数返回什么?
✅ 核心规则:async 函数总是返回一个 Promise 对象
async function testAsy() {
return 'hello world';
}
const result = testAsy();
console.log(result);
// 输出: Promise {<fulfilled>: "hello world"}
✅ 返回值的封装机制
无论 async 函数 return 什么,都会被自动包装成 Promise:
| 返回值类型 | 等价于 |
|---|---|
return 'hello' | return Promise.resolve('hello') |
return 42 | return Promise.resolve(42) |
return undefined | return Promise.resolve(undefined) |
return new Promise(...) | 直接返回该 Promise(不包装) |
throw new Error() | return Promise.reject(new Error()) |
async function demo() {
return 'Hello';
}
// 等价于:
function demoLegacy() {
return Promise.resolve('Hello');
}
三、await 的工作原理
✅ await 只能出现在 async 函数中
// ❌ 错误:顶层 await(仅在模块中可用)
// await fetch('/api/data');
// ✅ 正确
async function getData() {
const response = await fetch('/api/data');
const data = await response.json();
return data;
}
✅ await 的本质:暂停函数执行,等待 Promise 结果
async function fetchUser() {
console.log('1. 开始获取用户');
// 暂停函数执行,等待 fetch 完成
const response = await fetch('/api/user');
console.log('2. 获取响应');
const user = await response.json();
console.log('3. 解析用户数据:', user);
return user;
}
// 调用
fetchUser().then(user => {
console.log('4. 用户:', user);
});
执行流程图解:
1. 开始获取用户
↓
await fetch() → 发起请求,函数暂停
↓
请求完成 → 函数恢复执行
↓
2. 获取响应
↓
await json() → 解析数据,函数暂停
↓
解析完成 → 函数恢复执行
↓
3. 解析用户数据: {name: "John"}
↓
返回 user
↓
4. 用户: {name: "John"}
✅ 关键点:
await并不会阻塞整个程序,只暂停当前async函数的执行。
四、async/await vs Promise.then 链
✅ 场景:串行请求三个 API
❌ 使用 .then() 链
fetch('/api/user')
.then(res => res.json())
.then(user => fetch(`/api/posts?userId=${user.id}`))
.then(res => res.json())
.then(posts => fetch(`/api/comments?postId=${posts[0].id}`))
.then(res => res.json())
.then(comments => {
console.log('最终评论:', comments);
})
.catch(err => {
console.error('任一请求失败:', err);
});
- 问题:嵌套感强,逻辑跳跃;
- 错误处理集中,但调试困难。
✅ 使用 async/await
async function getComments() {
try {
const userRes = await fetch('/api/user');
const user = await userRes.json();
console.log('用户:', user);
const postsRes = await fetch(`/api/posts?userId=${user.id}`);
const posts = await postsRes.json();
console.log('文章:', posts);
const commentsRes = await fetch(`/api/comments?postId=${posts[0].id}`);
const comments = await commentsRes.json();
console.log('评论:', comments);
return comments;
} catch (err) {
console.error('请求失败:', err);
// 可以 throw err 继续向上抛
}
}
// 调用
getComments();
✅ 优势对比
| 特性 | .then() 链 | async/await |
|---|---|---|
| 可读性 | 一般,链式跳跃 | ✅ 极高,像同步代码 |
| 错误处理 | .catch() 统一捕获 | ✅ try/catch 更直观 |
| 调试 | 堆栈信息复杂 | ✅ 可以设断点,单步调试 |
| 变量作用域 | 需通过闭包传递 | ✅ 直接使用 let/const |
| 返回值 | 返回新 Promise | ✅ 返回 Promise,可 return |
五、async/await 的错误处理
✅ 使用 try/catch
async function safeFetch() {
try {
const res = await fetch('/api/data');
if (!res.ok) throw new Error(res.statusText);
const data = await res.json();
return data;
} catch (err) {
console.error('请求失败:', err.message);
return null; // 或抛出错误
}
}
✅ 可选:使用 .catch()(不推荐)
async function getData() {
const res = await fetch('/api/data').catch(err => {
console.error('网络错误:', err);
});
if (!res) return;
return res.json();
}
✅ 推荐:在
async函数内部使用try/catch,更清晰。
六、并发执行:避免“串行等待”
❌ 错误示范:不必要的串行
async function badExample() {
const user = await fetch('/api/user').then(r => r.json());
const posts = await fetch('/api/posts').then(r => r.json());
const comments = await fetch('/api/comments').then(r => r.json());
// 实际上可以并行!
}
✅ 正确做法:先发起所有请求,再 await
async function goodExample() {
// 并发发起请求
const userPromise = fetch('/api/user').then(r => r.json());
const postsPromise = fetch('/api/posts').then(r => r.json());
const commentsPromise = fetch('/api/comments').then(r => r.json());
// 等待所有完成
const [user, posts, comments] = await Promise.all([
userPromise,
postsPromise,
commentsPromise
]);
console.log({ user, posts, comments });
}
✅ 关键:
await只放在最后,避免阻塞后续请求。
七、async/await 的底层原理:Generator + Promise
async/await 本质上是 Generator 函数和 Promise 的语法糖。
// async/await 写法
async function fetchData() {
const res = await fetch('/api/data');
return await res.json();
}
// 等价于(简化版)
function fetchDataGenerator() {
const gen = function* () {
const res = yield fetch('/api/data');
const data = yield res.json();
return data;
};
const g = gen();
return new Promise((resolve, reject) => {
function next(val) {
try {
const result = g.next(val);
if (result.done) {
resolve(result.value);
} else {
result.value.then(next).catch(reject);
}
} catch (err) {
reject(err);
}
}
next();
});
}
🔍 虽然你不需要手动实现,但理解这一点有助于深入掌握其工作机制。
八、最佳实践
✅ 1. 总是使用 try/catch 处理可能出错的 await
try {
const data = await api.fetchData();
} catch (err) {
// 处理错误
}
✅ 2. 并发请求时,先 Promise.all 再 await
const [user, posts] = await Promise.all([
fetch('/user').then(r => r.json()),
fetch('/posts').then(r => r.json())
]);
✅ 3. 不要滥用 await
// ❌ 不必要的 await
const data = await Promise.resolve('hello');
// ✅ 直接使用
const data = 'hello';
💡 结语
“
async/await让异步代码回归了它本该有的样子——清晰、简洁、可读。”
它解决了:
.then()链的嵌套和跳跃;- 错误处理不直观;
- 调试困难等问题。
掌握 async/await,你就能写出:
- 更优雅的异步代码;
- 更健壮的错误处理;
- 更高效的并发逻辑。