前言
受够了回调地狱,又觉得Promise用起来不过瘾,想用一下更加优雅的async/await吗?在使用之前可以关注一下有哪些使用方式和坑。已经使用了async/await的同学也可以看一下有没有遇到过类似的坑。另外,本文是译文,喜欢看英文的同学可以直接去下文原文链接去看。
正文开始分割线
正文开始!
当我们在写async函数时,有几种不同的写法: await, return, return await。这几种写法实现的效果有所不同,选择一个正确的写法十分重要。
我人先从一个async函数开始:
async function waitAndMaybeReject() {
// 等一秒钟
await new Promise(r => setTimeout(r, 1000));
// 随机一把
const isHeads = Boolean(Math.round(Math.random()));
if (isHeads) return 'yay';
throw Error('Boo!');
}
上面的函数会返回一个等待一秒的promise,一秒之后,有50%的概率获得fullfill(resolve)值"yay",另外50%的概率reject一个error。接下来我们将通过不同的方式来使用这个函数:
直接调用
async function foo() {
try {
waitAndMaybeReject();
}
catch (e) {
return 'caught';
}
}
在这种用法中,如果你调用 foo 函数,返回的promise总是立即获得fullfill值undefined, 不会有等待时间。
因为我们没有对 waitAndMaybeReject() 的结果进行 await 或者 return,所以这个函数不会有期望的async效果。这种代码一般是错误的写法。
Awaiting
async function foo() {
try {
await waitAndMaybeReject();
}
catch (e) {
return 'caught';
}
}
在这种用法中,如果调用 foo 函数,返回的promise总是会等待一秒,接着,要么获得fullfill值undefined,要么获得fullfill值 "caught"。
因为我们await了 waitAndMaybeReject() 的返回结果,所以它的rejection相当于throw了一个异常,然后catch块就能捕获到这个异常。当 waitAndMaybeReject() 是正常的fullfill时,因为我们没对fullfill的值做任何处理,所以 foo 函数的fullfill值是undefined。
Retruning
async function foo() {
try {
return waitAndMaybeReject();
}
catch (e) {
return 'caught';
}
}
在这种用法中,如果调用 foo 函数,返回的promise总是会等待一秒,接着,要么获得fullfill值 "yay", 要么reject(Error('Boo!'))。
通过返回 waitAndMaybeReject() 的值,我们直接传递了它的返回结果(Promise),所以我们catch块里的代码永远不会执行。
Return-awaiting
如果你想在catch块中捕获到 waitAndMaybeReject() 的异常,在try中正常返回 waitAndMaybeReject() 的fullfill值,你得使用 return await:
async function foo() {
try {
return await waitAndMaybeReject();
}
catch (e) {
return 'caught';
}
}
在这种用法中,如果调用 foo 函数,返回的promise总是会等待一秒,接着,要么获得fullfill值 "yay",要么获得fullfill值 "caught"。
因为我们await了 waitAndMaybeReject() 的返回结果,所以它的rejection相当于throw了一个异常,我们的catch块就会执行。如果 waitAndMaybeReject() 获得了fullfill值("yay"),我们就将这个值返回。
如果上面的代码看起来令人费解,那么将代码拆分成两个部分来看可能会比较好理解:
async function foo() {
try {
// 等待 waitAndMaybeReject() 的结果来解决,
// 并且将 fullfill 的值赋给 fullfilledValue:
const fulfilledValue = await waitAndMaybeReject();
// 如果 waitAndMaybeReject() reject了,
// 我们的代码就会抛出异常,并且进入 catch 代码块的逻辑。
// 否则,这里的代码就会继续运行下面的语句:
return fulfilledValue;
}
catch (e) {
return 'caught';
}
}
注意:在 try/catch 块之外执行
return await是多余的操作(直接return就好啦)。甚至Eslint还专门有规则来检测这种场景,如果在 try/catch 块之内,Eslint就允许这种操作。(当然你也可以加eslint disable来无视这种操作限制)

