异步函数中的return undefined和return Promise.resolve()有什么区别?

时间:2018-12-12 03:22:41

标签: javascript promise async-await

大家好,我发现了一个有趣的东西,它带有异步函数的返回值。

有两个代码:

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

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

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与第一种情况不同,只是因为我们return Promise.resolve()在async2函数中。

顺便说一句,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()之间有什么区别?

1 个答案:

答案 0 :(得分:0)

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

参考规范:(运行代码段以查看规范部分。

如您所见,在步骤3.d.1中,该诺言立即得到解决(如步骤3.b.所述:声明:如果我们返回此处。(如您的第一个示例所示)< / p>

另一方面,当返回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>