异步函数返回值
264 Returning Values from Async Functions 学习笔记
一、核心知识点总览
本视频聚焦 JavaScript 中异步函数(Async Functions)返回值的特性、获取方式、错误处理及代码优化,解决“异步函数如何返回数据”“返回值为何是 Promise”“如何优雅处理异步返回结果”等关键问题,是异步编程进阶的核心内容。
二、异步函数返回值的本质(核心特性)
2.1 关键结论:异步函数永远返回 Promise
- 特性说明:无论异步函数(
async function声明)内部返回什么值(基本类型、对象、函数等),最终都会被 JavaScript 包装成一个 已兑现(fulfilled)的 Promise;若函数内部抛出错误,则返回一个 已拒绝(rejected)的 Promise。 - 原理拆解:
异步函数的执行逻辑是“非阻塞”的——调用异步函数时,JS 引擎会立即返回一个未完成的 Promise,然后在后台继续执行函数内部代码;当函数执行完毕(或遇到return/throw),再更新 Promise 的状态(兑现/拒绝)并传递结果。
2.2 对比:异步函数 vs 普通函数返回值
通过 console.log 观察执行顺序,可直观理解两者差异:
1 | // 1. 普通函数:同步执行,立即返回值 |
三、异步函数返回值的获取方式
3.1 方式1:使用 .then() 链式调用(兼容 Promise 旧语法)
-
适用场景:需要在异步函数执行完毕后,基于返回值做后续操作(如渲染页面、调用其他接口)。
-
语法示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 异步函数:模拟获取用户地理位置(返回城市名称)
async function getCity() {
// 模拟异步操作(如调用地理定位API)
await new Promise((resolve) => setTimeout(resolve, 1000));
return "葡萄牙 Olhao"; // 包装为 Promise.resolve("葡萄牙 Olhao")
}
// 调用异步函数,用 .then() 获取返回值
console.log("开始获取城市...");
getCity()
.then((city) => {
console.log("获取到城市:", city); // 1秒后执行:获取到城市:葡萄牙 Olhao
})
.catch((error) => {
console.error("获取失败:", error);
});
3.2 方式2:使用 await + 异步函数(推荐,更简洁)
-
核心规则:
await关键字必须在 另一个异步函数内部 使用,作用是“暂停当前异步函数的执行,等待目标 Promise 兑现后再继续”。 -
优化点:避免
.then()链式调用的嵌套问题(“回调地狱”),代码逻辑更接近同步写法。 -
语法示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15async function getCity() {
await new Promise((resolve) => setTimeout(resolve, 1000));
return "葡萄牙 Olhao";
}
// 用 async IIFE(立即执行异步函数)包裹 await(因顶层 await 需特殊配置)
(async function () {
console.log("开始获取城市...");
try {
const city = await getCity(); // 等待 getCity() 返回的 Promise 兑现
console.log("获取到城市:", city); // 1秒后执行:获取到城市:葡萄牙 Olhao
} catch (error) {
console.error("获取失败:", error);
}
})(); -
补充:若项目支持 ES2022+ 语法,可直接在模块顶层使用
await(无需 IIFE),如:1
2
3// 模块文件(.mjs 或 package.json 配置 "type": "module")
const city = await getCity();
console.log(city);
四、异步函数的错误处理
4.1 问题:异步函数内部错误默认不触发外部 catch
-
现象:若异步函数内部用
try/catch捕获了错误,但未重新抛出(throw),则函数返回的 Promise 仍为“已兑现”状态,外部.catch()或try/catch无法捕获该错误。 -
示例(错误写法):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19async function getCity() {
try {
await new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("定位失败")), 1000); // 模拟异步错误
});
return "葡萄牙 Olhao";
} catch (error) {
console.log("内部捕获错误:", error.message); // 仅内部打印,未重新抛出
}
}
// 外部调用:无法捕获内部错误
getCity()
.then((city) => {
console.log("城市:", city); // 执行:城市:undefined(因函数未返回有效值)
})
.catch((error) => {
console.error("外部捕获错误:", error); // 不执行!
});
4.2 解决方案:重新抛出错误(throw error)
-
原理:在异步函数的
catch块中重新抛出错误,会将函数返回的 Promise 状态改为“已拒绝”,外部即可通过.catch()或try/catch捕获。 -
示例(正确写法):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21async function getCity() {
try {
await new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("定位失败")), 1000);
});
return "葡萄牙 Olhao";
} catch (error) {
console.log("内部捕获错误:", error.message);
throw error; // 重新抛出,让外部能捕获
}
}
// 外部用 try/catch + await 捕获错误
(async function () {
try {
const city = await getCity();
console.log("城市:", city);
} catch (error) {
console.error("外部捕获错误:", error.message); // 执行:外部捕获错误:定位失败
}
})();
4.3 额外优化:用 finally 执行收尾操作
- 特性:
finally块中的代码无论 Promise 状态是“兑现”还是“拒绝”,都会执行(如关闭加载动画、清理资源)。 - 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16(async function () {
console.log("开始获取城市...");
try {
const city = await getCity();
console.log("城市:", city);
} catch (error) {
console.error("错误:", error.message);
} finally {
console.log("获取操作结束(无论成功/失败)"); // 必执行
}
})();
// 执行顺序:
// 1. 开始获取城市...
// 2. 内部捕获错误:定位失败
// 3. 错误:定位失败
// 4. 获取操作结束(无论成功/失败)
五、实战例题(巩固应用)
例题1:获取用户信息并渲染
需求:用两个异步函数分别获取“用户 ID”和“用户详情”,要求先获取 ID,再用 ID 查详情,最后打印结果;若任一环节失败,捕获错误并提示。
1 | // 异步函数1:获取用户ID(模拟接口调用) |
例题2:判断返回值类型
问题:以下代码执行后,result1 和 result2 分别是什么类型?为什么?
1 | async function func1() { |
答案:
result1是Promise类型(状态:fulfilled,值:123)。因为异步函数无论返回什么值,都会包装成已兑现的 Promise。result2是Promise类型(状态:rejected,值:Error 对象)。因为异步函数内部抛出错误,会包装成已拒绝的 Promise。
六、总结
- 返回值本质:异步函数永远返回 Promise,
return X等价于return Promise.resolve(X),throw E等价于return Promise.reject(E)。 - 获取方式:优先用
await + async(代码简洁),兼容场景用.then();await必须在异步函数内部使用。 - 错误处理:异步函数内部捕获错误后,需
throw error才能让外部捕获;finally用于执行收尾操作。 - 核心原则:避免混合
await和.then()语法,保持代码风格统一,降低可读性成本。
本文是原创文章,采用CC BY-NC-SA 4.0协议,完整转载请注明来自木鱼的鱼窝


