【问题标题】:What's the differences between return undefined & return Promise.resolve() in async function?异步函数中的 return undefined 和 return Promise.resolve() 有什么区别?
【发布时间】:2018-12-12 12:14:30
【问题描述】:

大家好~我发现异步函数的返回值很有趣。

有两个代码:

async function test () {
  return Promise.resolve(undefined)
}

async function test () {
  return undefined
}

那么它们之间有什么区别呢?

如果您像我一样说“它们是同一件事”,那么我对这段代码感到困惑:

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log('async2 start');
   // return Promise.resolve();
}
async1();
new Promise(function (resolve) {
  console.log("Promise 1 start");
  resolve();
}).then(function () {
  console.log("Then 1");
}).then(function () {
  console.log("Then 2");
}).then(function () {
  console.log("Then 3");
}).then(function () {
  console.log("Then 4");
});

在 Chrome 73 中,您将获得:

async1 start
async2 start
Promise 1 start
async1 end
Then 1
Then 2
Then 3
Then 4

现在如果我们在 async2 函数中return Promise.resolve()

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log('async2 start');
  return Promise.resolve();
}
async1();
new Promise(function (resolve) {
  console.log("Promise 1 start");
  resolve();
}).then(function () {
  console.log("Then 1");
}).then(function () {
  console.log("Then 2");
}).then(function () {
  console.log("Then 3");
}).then(function () {
  console.log("Then 4");
});

在 Chrome 73 中,您将获得:

async1 start
async2 start
Promise 1 start
Then 1
Then 2
async1 end
Then 3
Then 4

了不起?请注意,async1 end 与第一种情况不同,只是因为我们在 async2 函数中 return Promise.resolve()

顺便说一句,Chrome 70 中的代码与 Chrome 73 不同。

您将获得 Chrome 70 中的第一个

async1 start
async2 start
Promise 1 start
Then 1
Then 2
async1 end
Then 3
Then 4

您将获得 Chrome 70 中的第二个

async1 start
async2 start
Promise 1 start
Then 1
Then 2
Then 3
async1 end
Then 4

Chrome 70 和 73 的差异可能是这篇博客的原因: https://v8.dev/blog/fast-async

因此,Chrome 73 似乎是未来的正确行为。但是我还是一头雾水,异步函数中 return undefined 和 return Promise.resolve() 有什么区别?

【问题讨论】:

  • 如果您想获得更多的乐趣,请将asyncasync2()函数中不返回承诺(你的第一个例子)。
  • 除非我遗漏了什么,如果它是异步的,这不只是由于性质吗?
  • “那么它们之间有什么区别呢?” ???? explicit promise creation anti-pattern。 TL;DR - return ... 解析异步函数返回值(一个承诺)。 return Promise.resolve(...) 做同样的事情,只是它用另一个本身立即解决的承诺来解决
  • @Phil 如果是这样,return Promise.resolve() 将比return undefined 更快。而结果恰恰相反

标签: javascript promise async-await


【解决方案1】:

它们都返回完全相同的东西,只是在事件循环的不同时间。

引用规范:(运行代码 sn-p 以查看规范部分。

如您所见,在步骤 3.d.1 中,Promise 立即解决(如步骤 3.b 所说 断言:如果我们返回此处。(就像您的第一个示例一样)

另一方面,当返回 Promise 时,序列将继续,直到第 7 步运行 AsyncFunctionAwait (25.5.5.3) 并最终在第 8 步返回。 (就像你的第二个例子一样)

这是来自规范的参考: https://www.ecma-international.org/ecma-262/8.0/#sec-async-functions-abstract-operations-async-function-start

<emu-clause id="sec-async-functions-abstract-operations-async-function-start" aoid="AsyncFunctionStart">
  <h1><span class="secnum">25.5.5.2</span>AsyncFunctionStart ( <var>promiseCapability</var>, <var>asyncFunctionBody</var> )</h1>
  <emu-alg>
    <ol>
      <li>Let <var>runningContext</var> be the
        <emu-xref href="#running-execution-context" id="_ref_6309"><a href="#running-execution-context">running execution context</a></emu-xref>.</li>
      <li>Let <var>asyncContext</var> be a copy of <var>runningContext</var>.</li>
      <li>Set the code evaluation state of <var>asyncContext</var> such that when evaluation is resumed for that
        <emu-xref href="#sec-execution-contexts" id="_ref_6310"><a href="#sec-execution-contexts">execution context</a></emu-xref> the following steps will be performed:
        <ol type="a">
          <li>Let <var>result</var> be the result of evaluating <var>asyncFunctionBody</var>.</li>
          <li>Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done.</li>
          <li>Remove <var>asyncContext</var> from the
            <emu-xref href="#execution-context-stack" id="_ref_6311"><a href="#execution-context-stack">execution context stack</a></emu-xref> and restore the
            <emu-xref href="#sec-execution-contexts" id="_ref_6312"><a href="#sec-execution-contexts">execution context</a></emu-xref> that is at the top of the
            <emu-xref href="#execution-context-stack" id="_ref_6313"><a href="#execution-context-stack">execution context stack</a></emu-xref> as the
            <emu-xref href="#running-execution-context" id="_ref_6314"><a href="#running-execution-context">running execution context</a></emu-xref>.</li>
          <li>If <var>result</var>.[[Type]] is
            <emu-const>normal</emu-const>, then
            <ol>
              <li>Perform !&nbsp;
                <emu-xref aoid="Call" id="_ref_6315"><a href="#sec-call">Call</a></emu-xref>(<var>promiseCapability</var>.[[Resolve]],
                <emu-val>undefined</emu-val>, «
                <emu-val>undefined</emu-val>»).</li>
            </ol>
          </li>
          <li>Else if <var>result</var>.[[Type]] is
            <emu-const>return</emu-const>, then
            <ol>
              <li>Perform !&nbsp;
                <emu-xref aoid="Call" id="_ref_6316"><a href="#sec-call">Call</a></emu-xref>(<var>promiseCapability</var>.[[Resolve]],
                <emu-val>undefined</emu-val>, «<var>result</var>.[[Value]]»).</li>
            </ol>
          </li>
          <li>Else,
            <ol>
              <li>Assert: <var>result</var>.[[Type]] is
                <emu-const>throw</emu-const>.</li>
              <li>Perform !&nbsp;
                <emu-xref aoid="Call" id="_ref_6317"><a href="#sec-call">Call</a></emu-xref>(<var>promiseCapability</var>.[[Reject]],
                <emu-val>undefined</emu-val>, «<var>result</var>.[[Value]]»).</li>
            </ol>
          </li>
          <li>Return.</li>
        </ol>
      </li>
      <li>Push <var>asyncContext</var> onto the
        <emu-xref href="#execution-context-stack" id="_ref_6318"><a href="#execution-context-stack">execution context stack</a></emu-xref>; <var>asyncContext</var> is now the
        <emu-xref href="#running-execution-context" id="_ref_6319"><a href="#running-execution-context">running execution context</a></emu-xref>.</li>
      <li>Resume the suspended evaluation of <var>asyncContext</var>. Let <var>result</var> be the value returned by the resumed computation.</li>
      <li>Assert: When we return here, <var>asyncContext</var> has already been removed from the
        <emu-xref href="#execution-context-stack" id="_ref_6320"><a href="#execution-context-stack">execution context stack</a></emu-xref> and <var>runningContext</var> is the currently
        <emu-xref href="#running-execution-context" id="_ref_6321"><a href="#running-execution-context">running execution context</a></emu-xref>.</li>
      <li>Assert: <var>result</var> is a normal completion with a value of
        <emu-val>undefined</emu-val>. The possible sources of completion values are
        <emu-xref aoid="AsyncFunctionAwait" id="_ref_6322"><a href="#sec-async-functions-abstract-operations-async-function-await">AsyncFunctionAwait</a></emu-xref> or, if the async function doesn't await anything, the step 3.g above.</li>
      <li>Return.
      </li>
    </ol>
  </emu-alg>
</emu-clause>

【讨论】:

  • 但是为什么它的时间比 2 而不是 1 microtask 慢呢?
  • 额外的内部处理(内部函数调用、内部对象的创建等)延迟了第二个示例的 Promise 微任务更快地进入队列 - 因此它稍后完成。
  • 我可以理解为什么它很慢,但是两个微任务的速度很慢,这就是我不明白的原因。我认为它应该只比一个微任务慢?
猜你喜欢
  • 2014-05-24
  • 2011-08-26
  • 2015-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-25
  • 2020-12-20
  • 1970-01-01
相关资源
最近更新 更多