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; 至少有两个区别:

  1. 对象上的区别:
    • return await promise; 会先把promise的结果解出来,再构造成新的Promise
    • return await promise; 直接在promise的基础上构造Promise,也就是套了两个Promise(两层Promise的状态和结果是一致的)
  2. 时间上的区别:假设 bar() 函数耗时 10s
    • foo1() 中的写法会导致这10s消耗在 foo1() 函数的执行上
    • foo2() 的写法则会让10s的消耗在 foo2() 函数的调用者侧,也就是注释为main的匿名立即函数

 从对象上的区别看,不论怎样async函数都会构造新的Promise对象,有无await都节约不了内存;从时间上来看,总体的等待时长理论上是一样的,怎么写对结果都没啥影响嘛。

 举个不大恰当的例子:你的上司交给你一个重要任务让你完成后发邮件给他,你分析了下后发现任务需要同事A做一部分,遂找他。同事A完成他的部分需要2天。这个时候你有两个做法选择:一、做完自己的部分后等着A出结果,有结果后再发邮件回复上司;二、将自己的部分完成后汇报给上司,并跟和上司说已经告知A:让A等完成他的部分后直接回邮件给上司。

 如果,我是说假如果哈,如果,这个重要任务本来要求必须在12h内完成,但实际耗时了两天严重超标......请问上述例子中哪种做法更容易获取N+1大礼包?

到底怎么写?

 回到代码层,通过上述分析可以知道,一个主要是耗时归属问题,一个是async函数“总是”会返回的那个Promise对象不是由Promise.resolve()简单包装的(因为Promise.resolve(promise) === promise),可以得到两个编码指南:

强调下,async函数不是通过Pomise.resolve()简单包装的,其实进一步思考下也不难理解,因为它要考虑执行有异常的场景,甚至还可能根据不同的Promise状态做一些其他的操作(比如日志输出、埋点统计?我瞎猜的)

// 避免非必要的 return await 影响模块耗时统计的准确性
async function foo() {
  return bar();
}

// 除非你要处理执行过程中的异常
async function foo() {
  try {
    return await bar();
  } catch (_) {
    return null;
  }
}
// 或:
async function foo() {
  return bar().catch(() => null);
}

// 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 执行中的异常时。

全部评论

相关推荐

11-30 11:07
河南大学 Java
宇宙厂 测开 n*15
丘丘给个offer:有后选后
点赞 评论 收藏
分享
点赞 评论 收藏
分享
Yushuu:你的确很厉害,但是有一个小问题:谁问你了?我的意思是,谁在意?我告诉你,根本没人问你,在我们之中0人问了你,我把所有问你的人都请来 party 了,到场人数是0个人,誰问你了?WHO ASKED?谁问汝矣?誰があなたに聞きましたか?누가 물어봤어?我爬上了珠穆朗玛峰也没找到谁问你了,我刚刚潜入了世界上最大的射电望远镜也没开到那个问你的人的盒,在找到谁问你之前我连癌症的解药都发明了出来,我开了最大距离渲染也没找到谁问你了我活在这个被辐射蹂躏了多年的破碎世界的坟墓里目睹全球核战争把人类文明毁灭也没见到谁问你了😆
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务