原文地址:await vs return vs return await 作者:Jake Archibald
当编写异步函数的时候,await,return,return await 三者之间有一些区别,从中选取正确的方式是很重要的。我们从下面这个异步函数开始:
async function waitAndMaybeReject(){
// 等待 1 秒钟
await new Promise(resolve => setTimeout(resolve, 1000));
// 抛一枚硬币
const isHeads = Boolean(Math.round(Math.random()));
if(isHeads) return ‘yay’;
throw Error(‘Boo!’);
}
上面的函数会等待 1 秒钟后返回一个 promise,然后有 50% 的机会成功返回 yay 或者抛出一个 error。让我们用几种稍微不同的方式使用它。
直接调用
async function foo() {
try{
waitAndMaybeReject();
}catch(e){
return ‘caught’;
}
}
在此处,如果调用了 foo,返回的 promise 的状态始终都是 resolved,值也永远是 undefined,而且没有等待。由于我们没有 await,或者 return waitAndMaybeReject()的结果,所以我们无法对它做出任何反应。像这样的代码通常是错误的。
Awaiting
async function foo(){
try{
await waitAndMaybeReject();
}catch(e){
return ‘caught’;
}
}
在此处,如果调用了 foo,返回的 promise 将始终等待 1 秒钟,然后结果要么状态为 resolved,值为 undefined,要么状态为 resolved,值为 ”caught”。因为我们等待了 waitAndMaybeReject() 的返回值,所以它的 rejection 会被返回并且被抛出(throw),catch 的代码块就会执行。但无论如何,如果 waitAndMaybeReject() 没有报错而是顺利执行,我们依旧无法对它的返回值做任何事情。
Returning
async function foo() {
try {
return waitAndMaybeReject();
}
catch (e) {
return ‘caught’;
}
}
在此处,如果调用了 foo,返回的 promise 将始终等待 1 秒钟,然后结果要么是状态为 resolved,值为 ”yaa”,要么是状态是 reject,抛出错误 Error(‘Boo!’)。通过 return waitAndMaybeReject() 这行代码,我们直接传递了它的返回结果,所以我们的 catch 代码块永远不会执行。
Return-awaiting
如果你想在 try 代码块中得到带有正确返回值的 resolved 状态,在 catch 中捕获异常,那么正确的选择就是 return await。
async function foo() {
try {
return await waitAndMaybeReject();
}
catch (e) {
return ‘caught’;
}
}
在此处,如果调用 foo,返回的 promise 将始终等待 1 秒钟,然后结果要么是状态为 resolved,值为 ”yay”,要么是状态为 resolved,值为 ”caught” 因为我们等待了 waitAndMaybeReject() 的结果,所以它的异常 rejecttion 会被返回并且被抛出(throw),catch 的代码块就会执行。如果 waitAndMaybeReject() 顺利执行没有报错,就返它的结果。
如果对上面的内容还是觉着困惑,那么将代码拆分成两个步骤来看可能会比较好理解:
async function foo() {
try {
// 等待 waitAndMaybeReject() 的结果来解决,
// 并且将 fullfill 的值赋给 fullfilledValue:
const fulfilledValue = await waitAndMaybeReject();
// 如果 waitAndMaybeReject() reject 了,
// 我们的代码就会抛出异常,并且进入 catch 代码块的逻辑。
// 否则,这里的代码就会继续运行下面的语句:
return fulfilledValue;
}
catch (e) {
return ‘caught’;
}
}
Note: 在 try/catch 之外的代码块中执行 return await 是多余的 (如前所述,直接 return 即可),甚至 Eslint 还专门有规则来检测这种场景,但是在 try/catch 代码块之内,Eslint 就允许这种操作。