sam*_*ime 27

基本上,因为return await是多余的.

从实际使用async函数的稍高级别来看它:

const myFunc = async () => {
  return await doSomething();
};

await myFunc();
Run Code Online (Sandbox Code Playgroud)

任何async函数都已经返回a Promise,并且必须作为a处理Promise(直接作为a Promise或者也作为await-ing).

如果你await在函数内部,它是多余的,因为外面的函数也会await以某种方式,所以没有理由不发送它Promise,让外面的东西处理它.

它在语法上不对错或不正确,通常不会引起问题.这完全是多余的,这就是为什么linter触发它.

  • 我不确定“发送承诺并让外部事物处理它”是否 100% 正确。在我的测试中,通过直接返回 Promise 和等待然后返回值来创建新的 Promise:`function waitForN(time) { return new Promise((resolve,reject) => { setTimeout(() => {reject (new Error('拒绝')); }, 时间); }); } 异步函数测试() { myPromise = waitForN(1000); myPromise.catch(error => console.log(error)); 返回我的承诺;}` 这将导致一个被捕获的 Promise 和一个未被捕获的 Promise。 (2认同)

Bam*_*ieh 11

它在规则的前几行中描述:

在异步函数内,返回await是没用的.由于异步函数的返回值始终包含在Promise.resolve中,因此除了在总体Promise解析或拒绝之前添加额外时间之外,返回await实际上并不执行任何操作.这种模式几乎可以肯定是由于程序员忽略了异步函数的返回语义.资源

基本上返回或不返回,将导致相同的事情,因为await将解决promise,因此不需要返回已解析的状态.

和...之间的不同:

async function foo() {
  return bar();
}

async function bar() {
  await Promise.resolve();
  throw new Error('BEEP BEEP');
}

foo().catch(error => console.log(error.stack));


Error: BEEP BEEP
    at bar (<anonymous>:7:9)
Run Code Online (Sandbox Code Playgroud)

async function foo() {
  return await bar();
}

Error: BEEP BEEP
    at bar (<anonymous>:7:9)
    at async foo (<anonymous>:2:10)
Run Code Online (Sandbox Code Playgroud)

最终结果是相同的,尽管将值保存到变量中并且之后立即返回它是不是最好的想法(您向引擎添加额外的步骤:保存到变量,然后获取该变量的值引用,返回它,它还可以防止像某些情况下的尾调用优化等.

因为你没有使用指定的变量,所以不要分配它.

现在回到为什么linter在这种情况下没有错误:

  • 你可以使用linter无法预测的其他地方保存的值.

例如:

async function foo() {
  return bar();
}

async function bar() {
  await Promise.resolve();
  throw new Error('BEEP BEEP');
}

foo().catch(error => console.log(error.stack));


Error: BEEP BEEP
    at bar (<anonymous>:7:9)
Run Code Online (Sandbox Code Playgroud)

要么

async function foo() {
  return await bar();
}

Error: BEEP BEEP
    at bar (<anonymous>:7:9)
    at async foo (<anonymous>:2:10)
Run Code Online (Sandbox Code Playgroud)

  • 如果您在当前版本的 V8(例如当前的 node.js)上运行代码,“return wait”非常有用 - 返回之前的等待让 V8 组成完全异步。堆栈跟踪(没有“成本”,因为调用函数的上下文由于“await”而“免费”可用)。 (3认同)
  • 如果是这样的话,这似乎不能解释为什么 `const x = wait foo()` 比 `return wait foo()` 更好? (2认同)
  • 好吧,如果您所做的只是const x =然后返回它将能够检测到它,但我明白了.它完全相同,并且没有警告它的原因是,它不想在检测更复杂的用例时烦恼. (2认同)
  • *“在异步函数中,return await 是无用的”* -- 如果 `return` 语句位于 `try ... catch` 块内,则不会。在这种情况下,等待将允许您就地处理任何错误。 (2认同)
  • @Bamieh https://v8.dev/blog/fast-async -- 向下滚动到“零成本”;请注意,该功能现在默认启用,例如在当前的 Node.js 和 Chrome 版本中。 (2认同)

Tam*_*dus 11

更新:no-return-await规则自 以来已弃用v8.46.0。世界正在康复。

这里许多人建议省略等待和写入return foo();,但这是一个明显错误的建议。写作没有什么问题return await foo();;相反,省略await是代码中的一个错误,我将在下面解释。因此,请关闭该no-return-await规则,正如 ESLint 和 SonarCloud 团队同时意识到的那样,并弃用了它。

省略await会破坏try-catch块,导致finally块过早运行,并导致异步堆栈跟踪中丢失一些行。也会断裂explicit resource management。在我看来,跳过await是一种误导性的尝试,旨在实现某种毫无根据的“优化”的抽象意义,以代码为代价节省 6 个字符和一个微任务(编辑:ShortFuse 只是表明它实际上不是保存,而是在当前 v8 中添加了一个微任务)正确性。经验法则很简单:如果您有异步调用,请等待它,即使在 return 语句中也是如此。让我们坚持经验法则并始终使用return await,并关闭no-return-awaiteslint 规则。

现在是咆哮:我花了多少天时间向开发人员解释,他们认为自己catch在块没有运行时发现了 V8 错误,以及我花了多少长时间争论试图包装 V8 的拉取请求。 try 块中的异步函数体,但不添加那些缺少的等待。因此,我们发生了多起生产事故。我的开发人员不断指出该no-return-await规则,当我对其提出质疑时,他们在堆栈溢出上指出了这些具有 100K 秒声誉的答案,并表示“但是等待是多余的!不能等待!”。我更改了我们的规则集,添加和链接了文档,但开发人员往往相信更高的权威,所以我不得不与他们坐下来,一起运行这些示例,让他们相信这不是节点错误,你只需要等待。我每个季度都必须与每个新开发人员一起做这件事。这是一场职业噩梦,让 70 名高级开发人员相信他们的基本假设一直都是错误的。我不能责怪他们,因为他们依赖“信誉良好的消息来源”。所以我们需要在我们行业的核心解决这个问题,我很感激 ESLint 改变了方向。如果我们从一开始就务实地坚持始终return await asyncFoo();,我们今天就不会遇到任何这些问题,而且我还可以节省数百个工程时间和更多的理智。

小演示,显示堆栈跟踪中缺失的行,并演示了过早运行的finally块:

async function main() {
  console.log("\nStatcktrace with await shows testStackWithAwait:");
  await testStackWithAwait().catch(logStack);
  console.log("\nStatcktrace without await shows missing testStackWithoutAwait:");
  await testStackWithoutAwait().catch(logStack);
  console.log("\nFinally runs before try block ends when await is omitted:");
  await testFinallyWithoutAwait();
}

async function fnThatThrows() {
  await delay(1);
  throw new Error();
}

async function testStackWithoutAwait() {
  return fnThatThrows(); // bad
}

async function testStackWithAwait() {
  return await fnThatThrows(); // good
}

async function fnThatLogs() {
  await delay(1);
  console.log('  Running in try block');
}

async function testFinallyWithoutAwait() {
  try {
    return fnThatLogs(); // bad
  } finally {
    console.log('  Running in finally block');
  }
}

function logStack(e) {
  console.log(e.stack);
}

function delay(timeout, value) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(value);
    }, timeout);
  });
}

main().catch(console.error);
Run Code Online (Sandbox Code Playgroud)

在 Windows 上的 Chrome 103 和 120 上,我收到以下日志:

Statcktrace with await shows testStackWithAwait:
Error
    at fnThatThrows (https://stacksnippets.net/js:23:9)
    at async testStackWithAwait (https://stacksnippets.net/js:31:10)
    at async main (https://stacksnippets.net/js:14:3)

Statcktrace without await shows missing testStackWithoutAwait:
Error
    at fnThatThrows (https://stacksnippets.net/js:23:9)
    at async main (https://stacksnippets.net/js:16:3)

Finally runs before try block ends when await is omitted:
  Running in finally block
  Running in try block
Run Code Online (Sandbox Code Playgroud)


edv*_*hen 7

因为你可以

async function() {
  return foo();
}
Run Code Online (Sandbox Code Playgroud)

无论您返回的是精确值还是函数体内的另一个对象,async function始终返回的结果是PromisePromise

  • @YoniRabinovitch 不再是了。如果您在当前版本的 V8(例如当前的 node.js)上运行代码,“return wait”非常有用 - 返回之前的等待让 V8 组成完全异步。堆栈跟踪(没有“成本”,因为调用函数的上下文由于“await”而“免费”可用)。 (3认同)
  • 这是最好的答案! (2认同)