JS async/await 深入
深入理解async/await语法糖 · 难度:高级 · +15XP
JavaScript async/await 异步编程深入
async/await 是 ES2017(ES8)引入的异步编程语法糖,构建在 Promise 之上,让异步代码看起来和执行起来更像同步代码。它消除了 .then() 链式调用和回调地狱,大幅提升了异步代码的可读性和可维护性。
async 函数
- 在函数前加
async关键字,函数自动返回 Promise - 函数内的
return值会被包装为 resolved 的 Promise - 函数内抛出的异常会被包装为 rejected 的 Promise
// async 函数总是返回 Promise
async function getNumber() {
return 42; // 等价于 return Promise.resolve(42)
}
getNumber().then(num => console.log(num)); // 42
// async 函数中的异常会被自动捕获
async function failTest() {
throw new Error("出错了");
// 等价于 return Promise.reject(new Error("出错了"))
}
failTest().catch(err => console.error(err.message));
await 关键字
| 用法 | 说明 | 示例 |
|---|---|---|
await promise | 等待 Promise 完成并返回结果 | const res = await fetch(url) |
await 非Promise值 | 自动包装为 Promise | const x = await 42 |
| 只能在 async 函数内使用 | 顶层 await (ES2022) | 模块中可直接 await |
// await 等待 Promise 完成
async function fetchUser(id) {
const response = await fetch("/api/users/" + id);
if (!response.ok) throw new Error("HTTP " + response.status);
const user = await response.json();
return user;
}
// 对比:Promise.then() 写法
function fetchUserThen(id) {
return fetch("/api/users/" + id)
.then(response => {
if (!response.ok) throw new Error("HTTP " + response.status);
return response.json();
});
}
// 使用
fetchUser(1).then(user => console.log(user));
错误处理与并发
// try/catch 处理错误
async function loadData() {
try {
const [users, posts, comments] = await Promise.all([
fetch("/api/users").then(r => r.json()),
fetch("/api/posts").then(r => r.json()),
fetch("/api/comments").then(r => r.json())
]);
return { users, posts, comments };
} catch (err) {
console.error("数据加载失败:", err.message);
return { users: [], posts: [], comments: [] }; // 降级返回
}
}
// 并发与顺序执行的区别
async function demo() {
// 顺序执行(第二个请求等第一个完成)
const user = await fetchUser(1); // 耗时 1s
const posts = await fetchPosts(1); // 耗时 1s
// 总耗时:2s
// 并发执行(同时发起两个请求)
const [user2, posts2] = await Promise.all([
fetchUser(2),
fetchPosts(2)
]);
// 总耗时:1s
}
// 高级模式:超时控制
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const res = await fetch(url, { signal: controller.signal });
return await res.json();
} finally {
clearTimeout(timeoutId);
}
}