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

IT技术 javascript async-await
2021-02-02 00:09:05

鉴于下面的代码示例,行为上是否有任何差异,如果有,这些差异是什么?

return await promise

async function delay1Second() {
  return (await delay(1000));
}

return promise

async function delay1Second() {
  return delay(1000);
}

据我了解,第一个将在异步函数中进行错误处理,错误会从异步函数的 Promise 中冒泡出来。但是,第二个需要少一个刻度。这个对吗?

这段代码只是一个返回 Promise 以供参考的常用函数。

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}
6个回答

大多数情况下,return之间没有明显的区别return await的两个版本都delay1Second具有完全相同的可观察行为(但根据实现,该return await版本可能会使用更多的内存,因为Promise可能会创建一个中间对象)。

但是,正如@PitaJ 指出的那样,有一种情况存在差异:如果returnorreturn 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!'
  }
}

在第一个版本中,async 函数在返回结果之前等待被拒绝的 promise,这导致拒绝变成异常并到达catch子句;因此,该函数将返回解析为字符串“已保存!”的Promise。

然而,该函数的第二个版本确实直接返回被拒绝的Promise,而无需在 async 函数中等待它,这意味着调用catchcase而是调用者获得拒绝。

我在一个场景中发现,return new Promise(function(resolve, reject) { })for...of循环内使用,然后resolve()在循环内调用之后pipe()不会暂停程序执行,直到管道完成,根据需要,但是使用await new Promise(...)确实如此。后者甚至是有效/正确的语法吗?它是“简写”return await new Promise(...)吗?你能帮我理解为什么后者有效而前者无效吗?对于背景下,方案是在solution 02这个答案
2021-03-14 00:09:05
也许还提到堆栈跟踪会有所不同(即使没有 try/catch)?我认为这是人们在这个例子中最常遇到的问题:]
2021-03-29 00:09:05

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

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

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

async function delay1Second() {
  return delay(1000);
}

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

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

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

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

显着差异:在不同的地方处理 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'

  • 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'

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

详细步骤:

回报Promise

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

返回等待Promise

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

词汇表:

  • 定居:Promise.[[PromiseStatus]]pending变为resolvedrejected
解释的很漂亮!逐步包装和拆开 Promise 使差异变得一目了然。从中获得的重要收获之一是 Promise.resolve 在传递Promise时返回的值。我最初认为它会返回一个已解决的Promise,但不,它会按原样返回Promise。
2021-03-27 00:09:05

这是一个很难回答的问题,因为它实际上取决于您的转译器(可能babel)实际如何呈现async/await. 无论如何都清楚的事情:

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

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

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

两者都返回一个 Promise,但第一个“Promise”一个原始值,第二个“Promise”一个 Promise。如果您await在某个呼叫站点上分别执行这些操作,结果将大不相同。
2021-03-17 00:09:05
为什么函数的行为相同?第一个返回一个已解析的值 ( undefined),第二个返回一个Promise
2021-03-29 00:09:05
@Amit 两个函数都返回一个 Promise
2021-04-02 00:09:05
确认。这就是我无法忍受的原因async/await——我发现这更难以推理。@PitaJ 是正确的,两个函数都返回一个 Promise。
2021-04-03 00:09:05
如果我用 a 包围两个异步函数的主体try-catch怎么办?在这种return promise情况下, anyrejection不会被捕获,正确,而在这种return await promise情况下,它会被捕获,对吗?
2021-04-09 00:09:05

在这里我留下了一些实用的代码,你可以理解它的不同之处

 let x = async function () {
  return new Promise((res, rej) => {
    setTimeout(async function () {
      console.log("finished 1");
      return await new Promise((resolve, reject) => { // delete the return and you will see the difference
        setTimeout(function () {
          resolve("woo2");
          console.log("finished 2");
        }, 5000);
      });
      res("woo1");
    }, 3000);
  });
};

(async function () {
  var counter = 0;
  const a = setInterval(function () { // counter for every second, this is just to see the precision and understand the code
    if (counter == 7) {
      clearInterval(a);
    }

    console.log(counter);
    counter = counter + 1;
  }, 1000);
  console.time("time1");
  console.log("hello i starting first of all");
  await x();
  console.log("more code...");
  console.timeEnd("time1");
})();

函数“x”只是一个异步函数,而不是其他 fucn 如果将删除返回它打印“更多代码...”

变量x只是一个异步函数,反过来又具有另一个异步函数,在代码的主要部分我们调用了一个wait来调用变量x的函数,当它完成时它遵循代码的顺序,这将是正常的对于“async/await”,但在x函数内部还有另一个异步函数,这会返回一个promise或返回一个“promise”,它会留在x函数内部,忘记主代码,即不会打印" console.log ("more code .."),另一方面,如果我们放置"await",它将等待每个函数完成并最终遵循主代码的正常顺序。

在“console.log(”完成1“删除”返回“下方,您将看到行为。

虽然此代码可能会解决问题,但包括解释如何以及为何解决问题将真正有助于提高您的帖子质量,并可能导致更多的投票。请记住,您是在为将来的读者回答问题,而不仅仅是现在问的人。编辑您的答案以添加解释并说明适用的限制和假设。
2021-04-03 00:09:05