JavaScript中return await究竟有无用武之地?
有没有区别?
先上一个 Demo,看看async
函数中return
时加和不加await
有没有区别:
function bar() {
return Promise.resolve("this from bar().");
}
async function foo1() {
return await bar(); // CASE#1 with await
}
async function foo2() {
return bar(); // CASE#2 without await
}
// main
(() => {
foo1().then((res) => {
console.log("foo1:", res); // res is string: 'this from bar().'
});
foo2().then((res) => {
console.log("foo2:", res); // res is string: 'this from bar().'
});
})();
可能在一些社区或团队的编程规范中,有明确要求:不允许使用非必要的 return await
。给出的原因是这样做对于 foo 函数而言,会增加等待 bar 函数返回的 Promise 出结果的时间(但其实它可以不用等,因为马上就要 return 了嘛,这个时间应留给 foo 函数的调用者去等)。
觉得上面的文字读起来很晦涩,可以直接带着问题看代码,问:以上例子中,foo1()
函数和foo2()
函数的写法对程序的执行过程有何影响?
先说结论:async
函数中 return await promise;
和 return promise;
从宏观结果来看是一样的,但微观上看有区别。
有什么区别?
基于上面的 Demo 改造一下,做个实验:
const TAG = Symbol();
const RESULT = Promise.resolve("return from bar().");
RESULT[TAG] = "TAG#RESULT";
function bar() {
return RESULT;
}
async function foo1() {
return await bar();
}
async function foo2() {
const foo2Ret = bar();
console.log("foo2Ret(i):", foo2Ret[TAG], foo2Ret === RESULT); // 'TAG#RESULT', true (1)
return foo2Ret; // without await
}
// main
(() => {
const foo1Ret = foo1();
console.log("foo1Ret:", foo1Ret[TAG], foo1Ret === RESULT); // undefined, false (2)
console.log("--------------------------------------------");
const foo2Ret = foo2();
console.log("foo2Ret(o):", foo2Ret[TAG], foo2Ret === RESULT); // undefined, false (3)
})();
从注释标注的执行结果可以看到:
- (1)处没有疑问,
foo2Ret
本来就是RESULT
; - (2)处应该也没有疑问,
foo1Ret
是基于RESULT
这个 Promise 的结果重新包装的一个新的 Promise(只是这个 Promise 的结果和 Result 是一致的); - (3)处应该和常识相悖,竟然和
(2)
不一样?是的,对于async
函数不管 return 啥都会包成 Promise,而且不是简单的通过Pomise.resolve()
包装。
那就可以得出结论了,async
函数中 return await promise;
和 return promise;
至少有两个区别:
- 对象上的区别:
return await promise;
会先把promise
的结果解出来,再构造成新的 Promisereturn promise;
直接在promise
的基础上构造 Promise,也就是套了两个 Promise(两层 Promise 的状态和结果是一致的)
- 时间上的区别:假设
bar()
函数耗时10s
foo1()
中的写法会导致这 10s 消耗在foo1()
函数的执行上foo2()
的写法则会让 10s 的消耗在foo2()
函数的调用者侧,也就是注释为 main 的匿名立即函数
从对象上的区别看,不论怎样async
函数都会构造新的 Promise 对象,有无await
都节约不了内存;从时间上来看,总体的等待时长理论上是一样的,因为两种写法对结果都没啥影响嘛。
但是,笔者这里举个不那么恰当的例子:假如你的 Leader 交给你一个重要任务,需要你完成后发邮件给他。你分析了下,后发现该任务需要同事 A 做一部分,遂找他;同事 A 说要完成他那部分需要 2 天。
此时的你有两个做法可供参考:
- 做完自己的部分后等着 A 出结果,有结果后再发邮件回复 Leader;
- 将自己的部分完成后汇报给 Leader,并和 Leader 同步 A 部分的情况:即告知 Leader 让 A 需要额外耗时,并已让他在完成后直接发邮件给 Leader。
假如(我是说假如哈~),这个任务非常重要,DDL 要求在 12H 内完成,但实际耗时两天严重超标,给团队造成了极大损失......那么请问,上述哪种做法更容易获取 N+1 大礼包?
到底怎么写?
回到代码层,通过上述分析可以知道,一个主要是耗时归属问题,一个是 async 函数“总是”会返回的那个 Promise 对象不是由Promise.resolve()
简单包装的(因为Promise.resolve(promise) === promise
),可以得到两个编码指南:
async
函数中应该尽可能避免多余的Promise.resolve()
包装- 只有在需要使用
try-catch-finaly
处理逻辑时才使用return await
- 如异常捕获、执行记录等
补充下,async 函数不是通过 Pomise.resolve()简单包装的,其实进一步思考下也不难理解,因为 JS 运行时需要考虑执行有异常的场景,甚至还可能要根据不同的 Promise 状态做一些其他的操作(比如调试日志输出、埋点统计?未查源码纯属瞎猜)
// 避免return await 影响模块耗时统计,和额外资源消耗
async function foo() {
return bar();
}
//(1)但需要消费执行过程中的异常时
async function foo() {
try {
return await bar();// 同 bar().catch(() => null);
} catch (_) {
return null;
}
}
//(2)以及需要记录执行状态或统计执行时间的时候
async function foo() {
// 以Loading状态为例
isLoading = true;
try {
return await bar();
} finaly{
isLoading = false;
}
}
// async 函数中避免对返回值再使用多余的 Pomise 包装
async function bar() {
return 'this is from bar().'; // YES
}
async function bar() {
return Promise.resolve('this is from bar().'); // !!! NO !!!
}
最后回到标题:JavaScript 中return await
有无用武之地?
答:有的,当在需要消化掉依赖的 Promise
中的异常,或需要记录依赖的 Promise
的执行状态(如最终状态、运行耗时)时。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南