Promise的语法糖async/await
七崽爱吃小饼干2026/01/29阅读 2专栏 JavaScript
JavaScript 中的 async/await 语法是处理异步操作的语法糖(底层还是基于 Promise 实现),核心作用是让异步代码写起来、读起来像同步代码一样直观,解决了传统 Promise 链式调用(.then())可能出现的“回调地狱”问题。
一、先搞懂前置知识:Promise
async/await 离不开 Promise:
- Promise 是处理异步操作的对象,有三种状态:
pending(进行中)、fulfilled(成功完成)、rejected(失败) - 异步操作成功用
resolve()返回结果,失败用reject()返回错误 - 可以用
.then()接收成功结果,.catch()捕获错误
举个简单的 Promise 示例(模拟接口请求):
javascript
// 模拟一个异步请求:获取用户信息
const getUserInfo = () => {
return new Promise((resolve, reject) => {
// 用 setTimeout 模拟网络延迟(异步操作)
setTimeout(() => {
const success = true; // 模拟请求成功/失败
if (success) {
resolve({ name: "张三", age: 25 }); // 成功:返回用户信息
} else {
reject(new Error("获取用户信息失败")); // 失败:返回错误信息
}
}, 1000);
});
};
// 调用 Promise(传统 .then() 方式)
getUserInfo()
.then((user) => {
console.log("用户信息:", user);
})
.catch((err) => {
console.error("错误:", err);
});
这种方式已经比回调函数好很多,但如果有多个依赖的异步操作(比如先获取用户信息,再根据用户 ID 获取订单),就会出现 .then() 嵌套,代码可读性下降,这时候 async/await 就派上用场了。
二、async 关键字
首先,async 关键字用于修饰函数,有两个核心作用:
- 被
async修饰的函数,返回值一定是一个 Promise 对象(无论你函数内部 return 了什么,JS 都会自动把它包装成 Promise) - 只有在
async函数内部,才能使用await关键字(await不能单独在普通函数中使用)
示例:async 函数的返回值
javascript
// 1. async 函数返回普通值
async function fn1() {
return "hello async";
}
// 调用 fn1,返回的是 Promise 对象,可用 .then() 接收
fn1().then((res) => {
console.log(res); // 输出:hello async
});
// 2. async 函数直接返回 Promise
async function fn2() {
return new Promise((resolve) => {
resolve("hello promise");
});
}
fn2().then((res) => {
console.log(res); // 输出:hello promise
});
三、await 关键字
await 顾名思义就是“等待”,它只能用在 async 函数内部,核心作用是:
等待一个 Promise 对象的状态变为 fulfilled(成功),并返回 Promise 内部 resolve() 的结果;如果 Promise 状态变为 rejected(失败),则会抛出错误,需要用 try/catch 捕获。
语法格式
javascript
async function 函数名() {
try {
// 等待 Promise 成功,并接收结果
const 结果 = await 一个Promise对象;
} catch (err) {
// 捕获 Promise 失败的错误
console.error(err);
}
}
示例1:用 async/await 改写上面的 Promise 示例
javascript
// 模拟异步请求(和之前一样的 Promise 函数)
const getUserInfo = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve({ name: "张三", age: 25 });
} else {
reject(new Error("获取用户信息失败"));
}
}, 1000);
});
};
// 用 async/await 调用异步操作
async function getResult() {
try {
// 等待 getUserInfo 的 Promise 完成,直接接收结果(像同步代码一样)
const user = await getUserInfo();
console.log("用户信息:", user); // 1秒后输出:用户信息: { name: '张三', age: 25 }
} catch (err) {
// 捕获异步操作的错误
console.error("错误:", err);
}
}
// 调用 async 函数
getResult();
示例2:多个依赖异步操作(无嵌套,更直观)
比如:先获取用户信息,再根据用户姓名获取用户订单,两个异步操作有依赖关系:
javascript
// 1. 模拟获取用户信息(Promise)
const getUserInfo = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ name: "张三", age: 25 });
}, 1000);
});
};
// 2. 模拟根据用户名获取订单(Promise,依赖用户名)
const getUserOrder = (userName) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{ orderId: "001", goods: "手机" },
{ orderId: "002", goods: "电脑" }
]);
}, 1000);
});
};
// 3. 用 async/await 处理依赖异步操作
async function getuserAllData() {
try {
// 第一步:获取用户信息(等待1秒)
const user = await getUserInfo();
console.log("获取到用户:", user.name);
// 第二步:根据用户名获取订单(等待1秒,依赖第一步的结果)
const orders = await getUserOrder(user.name);
console.log("获取到用户订单:", orders);
} catch (err) {
console.error("错误:", err);
}
}
// 调用函数:总共耗时2秒,按顺序输出结果
getuserAllData();
如果用传统的 .then() 实现,会出现嵌套:
javascript
// .then() 链式调用(有嵌套,可读性不如 async/await)
getUserInfo()
.then((user) => {
console.log("获取到用户:", user.name);
return getUserOrder(user.name); // 返回下一个 Promise
})
.then((orders) => {
console.log("获取到用户订单:", orders);
})
.catch((err) => {
console.error("错误:", err);
});
对比之下,async/await 的代码结构更清晰,逻辑更易理解。
四、关键注意点
await只能等待 Promise:如果await后面不是 Promise 对象,它会直接返回该值(相当于同步执行)javascriptasync function fn() { const res = await "不是Promise"; console.log(res); // 直接输出:不是Promise } fn();async/await不会阻塞全局代码:await只会阻塞当前async函数内部的代码执行(等待异步操作完成),不会影响函数外部的全局代码执行javascriptasync function fn() { console.log("开始执行异步操作"); await new Promise((resolve) => setTimeout(resolve, 1000)); console.log("异步操作完成(内部阻塞)"); } // 调用 async 函数 fn(); // 全局代码不会被阻塞,会先于 await 内部的后续代码执行 console.log("全局代码执行"); // 输出顺序: // 开始执行异步操作 // 全局代码执行 // (1秒后)异步操作完成(内部阻塞)- 错误处理的两种方式:
- 方式1:
try/catch(推荐,更贴合同步代码的错误处理逻辑,适合处理单个或少数异步操作) - 方式2:给
await的 Promise 后面加.catch()(适合多个独立异步操作的错误单独处理)javascriptasync function fn() { // 单个异步操作的错误捕获(方式2) const user = await getUserInfo().catch((err) => { console.error("获取用户失败:", err); return null; // 返回默认值,避免后续代码报错 }); if (user) { console.log(user); } }
- 方式1:
- 并行执行多个独立异步操作:如果多个异步操作之间没有依赖关系,不要用串行的
await(会浪费时间),可以用Promise.all()实现并行执行javascript// 两个无依赖的异步操作 const fnA = () => new Promise((resolve) => setTimeout(() => resolve("A完成"), 1000)); const fnB = () => new Promise((resolve) => setTimeout(() => resolve("B完成"), 1000)); // 错误:串行执行(总共耗时2秒) async function serialFn() { const resA = await fnA(); const resB = await fnB(); console.log(resA, resB); } // 正确:并行执行(总共耗时1秒) async function parallelFn() { // 先同时启动所有异步操作 const promiseA = fnA(); const promiseB = fnB(); // 再等待所有结果返回 const resA = await promiseA; const resB = await promiseB; // 或者直接用 Promise.all() // const [resA, resB] = await Promise.all([fnA(), fnB()]); console.log(resA, resB); } parallelFn();
总结
async/await是 Promise 的语法糖,用于简化异步代码的编写,让异步代码更接近同步逻辑。async修饰函数,使其返回 Promise,且允许内部使用await;await等待 Promise 完成并返回结果,失败则抛出错误。- 错误处理优先用
try/catch,无依赖异步操作用Promise.all()并行执行以提高效率。