`return await promise`和`return promise`之间的区别

Pit*_*taJ 65 javascript async-await

鉴于下面的代码示例,行为是否存在差异,如果是,那么这些差异是什么?

return await promise

async function delay1Second() {
  return (await delay(1000));
}
Run Code Online (Sandbox Code Playgroud)

return promise

async function delay1Second() {
  return delay(1000);
}
Run Code Online (Sandbox Code Playgroud)

据我了解,第一个会在异步函数中进行错误处理,并且错误会从异步函数的Promise中冒出来.但是,第二个需要少一个滴答.它是否正确?

此片段只是返回Promise以供参考的常用函数.

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}
Run Code Online (Sandbox Code Playgroud)

Den*_*ton 105

大多数时候,return和之间没有可观察到的差异return await.两个版本都delay1Second具有完全相同的可观察行为(但根据实现,return await版本可能会使用稍多的内存,因为Promise可能会创建一个中间对象).

但是,正如@PitaJ指出的那样,有一种情况存在差异:如果return或者return await嵌套在try- catch块中.考虑这个例子

async function rejectionWithReturnAwait () {
  try {
    return await Promise.reject(new Error())
  } catch (e) {
    return 'Saved!'
  }
}

async function rejectionWithReturn () {
  try {
    return Promise.reject(new Error())
  } catch (e) {
    return 'Saved!'
  }
}
Run Code Online (Sandbox Code Playgroud)

在第一个版本中,异步函数在返回结果之前等待被拒绝的promise,这会导致拒绝被转换为异常和catch要达到的子句; 因此,该函数将返回一个解析为"已保存!"字符串的promise.

但是,函数的第二个版本确实直接返回被拒绝的promise,而不是在async函数中等待它,这意味着调用catchcase 并且调用者获得拒绝.

  • 也许还提到堆栈跟踪会有所不同(即使没有 try/catch)?我认为这是人们在这个例子中最常遇到的问题:] (3认同)

chh*_*vey 19

正如其他答案所提到的,通过直接返回承诺让承诺冒泡可能会带来轻微的性能优势 - 仅仅是因为您不必先等待结果然后再次用另一个承诺包装它。然而,还没有人谈论过尾调用优化

尾调用优化,或“适当的尾调用”,是解释器用来优化调用栈的一种技术。目前,支持它的运行时还不多——尽管它在技术上是ES6 标准的一部分——但将来可能会添加支持,因此您可以通过现在编写好的代码来为此做好准备。

简而言之,TCO(或 PTC)通过为另一个函数直接返回的函数打开新框架来优化调用堆栈。相反,它重用相同的框架。

async function delay1Second() {
  return delay(1000);
}
Run Code Online (Sandbox Code Playgroud)

由于delay()由 直接返回delay1Second(),因此支持 PTC 的运行时将首先为delay1Second()(外部函数)打开一个框架,但随后不会为(内部函数)打开另一个框架delay(),它只会重用为外部函数打开的同一个框架。这优化了堆栈,因为它可以防止具有非常大的递归函数的堆栈溢出(hehe),例如,fibonacci(5e+25)。从本质上讲,它变成了一个循环,速度要快得多。

PTC 只有在直接返回内部函数时才启用。当函数的结果在返回之前被改变时,它不会被使用,例如,如果你有return (delay(1000) || null), 或return await delay(1000)

但就像我说的,大多数运行时和浏览器还不支持 PTC,所以现在它可能不会有太大的不同,但它不会对你的代码面向未来造成伤害。

在这个问题中阅读更多内容:Node.js:异步函数中的尾调用是否有优化?


Rag*_*ime 7

显着差异:在不同的地方处理 Promise 拒绝

  • return somePromisesomePromise传递给调用站点,并将await somePromise 传递给调用站点(如果有的话)。因此,如果 somePromise 被拒绝,则不会由本地 catch 块处理,而是由调用站点的 catch 块处理。

async function foo () {
  try {
    return Promise.reject();
  } catch (e) {
    console.log('IN');
  }
}

(async function main () {
  try {
    let a = await foo();
  } catch (e) {
    console.log('OUT');
  }
})();
// 'OUT'
Run Code Online (Sandbox Code Playgroud)

  • return await somePromise将首先等待somePromise在本地解决。因此,值或异常将首先在本地处理。=> 如果somePromise被拒绝,将执行本地 catch 块。

async function foo () {
  try {
    return await Promise.reject();
  } catch (e) {
    console.log('IN');
  }
}

(async function main () {
  try {
    let a = await foo();
  } catch (e) {
    console.log('OUT');
  }
})();
// 'IN'
Run Code Online (Sandbox Code Playgroud)

原因:return await Promise在本地和外部都return Promise等待,只在外部等待

详细步骤:

回报承诺

async function delay1Second() {
  return delay(1000);
}
Run Code Online (Sandbox Code Playgroud)
  1. 打电话delay1Second();
const result = await delay1Second();
Run Code Online (Sandbox Code Playgroud)
  1. 在内部delay1Second(),函数delay(1000)立即返回一个承诺[[PromiseStatus]]: 'pending。让我们称之为delayPromise
async function delay1Second() {
  return delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Run Code Online (Sandbox Code Playgroud)
  1. 异步函数会将它们的返回值包装在Promise.resolve()( Source ) 中。因为delay1Second是一个异步函数,我们有:
const result = await Promise.resolve(delayPromise); 
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
Run Code Online (Sandbox Code Playgroud)
  1. Promise.resolve(delayPromise)delayPromise不做任何事情就返回,因为输入已经是一个承诺(参见MDN Promise.resolve):
const result = await delayPromise; 
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
Run Code Online (Sandbox Code Playgroud)
  1. await等到问题delayPromise解决。
  • IFdelayPromise满足 PromiseValue=1:
const result = 1; 
Run Code Online (Sandbox Code Playgroud)
  • ELSEdelayPromise被拒绝:
// jump to catch block if there is any
Run Code Online (Sandbox Code Playgroud)

返回等待承诺

async function delay1Second() {
  return await delay(1000);
}
Run Code Online (Sandbox Code Playgroud)
  1. 打电话delay1Second();
const result = await delay1Second();
Run Code Online (Sandbox Code Playgroud)
  1. 在内部delay1Second(),函数delay(1000)立即返回一个承诺[[PromiseStatus]]: 'pending。让我们称之为delayPromise
async function delay1Second() {
  return await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Run Code Online (Sandbox Code Playgroud)
  1. 本地等待将等到delayPromise解决。
  • 情况 1 :delayPromise满足 PromiseValue=1:
async function delay1Second() {
  return 1;
}
Run Code Online (Sandbox Code Playgroud)
const result = await Promise.resolve(1); // let's call it "newPromise"
Run Code Online (Sandbox Code Playgroud)
const result = await newPromise; 
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: 1
Run Code Online (Sandbox Code Playgroud)
const result = 1; 
Run Code Online (Sandbox Code Playgroud)
  • 情况2delayPromise被拒绝:
// jump to catch block inside `delay1Second` if there is any
// let's say a value -1 is returned in the end
Run Code Online (Sandbox Code Playgroud)
const result = await Promise.resolve(-1); // call it newPromise
Run Code Online (Sandbox Code Playgroud)
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: -1
Run Code Online (Sandbox Code Playgroud)
const result = -1;
Run Code Online (Sandbox Code Playgroud)

词汇表:

  • 结算:Promise.[[PromiseStatus]]pending变为resolvedrejected


小智 7

在我们的项目中,我们决定始终使用“return wait”。争论的焦点是“当稍后在 return 表达式周围放置 try-catch 块时忘记添加 'await' 的风险证明现在有多余的 'await' 是合理的。”

  • 我100%同意。还向新加入者解释*在调用异步函数时总是使用await,除非它立即返回,除非它处于try-catch*中,这真是荒谬。 (3认同)

nra*_*itz 5

这是一个很难回答的问题,因为它实际上取决于您的转译器(可能babel)实际渲染的方式async/await。无论如何,明确的事情是:

  • 两个实现的行为应该相同,尽管第一个实现在链中可能少一个。Promise

  • 特别是如果您删除不必要的await,第二个版本将不需要转译器中的任何额外代码,而第一个版本则需要。

因此,从代码性能和调试的角度来看,第二个版本更可取,尽管只是非常轻微,而第一个版本具有轻微的易读性优势,因为它清楚地表明它返回一个承诺。

  • @Amit 两个函数都返回一个 Promise (4认同)