带有 += 的异步函数

ALD*_*ENT 65 javascript event-loop async-await

let x = 0;

async function test() {
    x += await 5;
    console.log('x :', x);
}

test();
x += 1;
console.log('x :', x);
Run Code Online (Sandbox Code Playgroud)

x记录的值为15。我的问题是:为什么x 5第二个日志的值是?

如果test是后执行x += 1(因为它是一个异步函数),那么价值x1由时间test执行,所以x += await 5应的值x 6

FZs*_*FZs 62

TL;DR:因为在它的第二个操作数(右侧)中有关键字,所以先+=读取x,但在更改后写入await


async函数在调用时同步运行,直到第一条await语句。

所以,如果你 remove await,它的行为就像一个普通的函数(除了它仍然返回一个 Promise )。

在这种情况下,您将在控制台中获得5(从函数)和6(从主脚本):

let x = 0;

async function test() {
  x += 5;
  console.log('x :', x);
}

test();
x += 1;
console.log('x :', x);
Run Code Online (Sandbox Code Playgroud)


第一个await停止同步运行,即使它的参数同步可用,因此以下将返回1(从主脚本)和6(从函数),如您所料:

let x = 0;

async function test() {
  // Enter asynchrony
  await 0;

  x += 5;
  console.log('x :', x);
}

test();
x += 1;
console.log('x :', x);
Run Code Online (Sandbox Code Playgroud)


但是,您的情况要复杂一些。

你已经放入await了一个表达式,它使用+=.

您可能知道,JSx += y中的x = (x + y). 为了更好地理解,我将使用后一种形式:

let x = 0;

async function test() {
  x = (x + await 5);
  console.log('x :', x);
}

test();
x += 1;
console.log('x :', x);
Run Code Online (Sandbox Code Playgroud)

当口译员到达这一行时......

x = (x + await 5);
Run Code Online (Sandbox Code Playgroud)

...它开始评估它,替代品x,所以它变成...

x = (0 + await 5);
Run Code Online (Sandbox Code Playgroud)

...然后,它到达await并停止。

函数调用后的代码开始运行,修改 的值x,然后记录下来。

x现在是1

然后,在主脚本退出后,解释器返回到暂停的test函数,并继续评估该行:

x = (0 + 5);
Run Code Online (Sandbox Code Playgroud)

并且,由于 的值x已经被替换,它仍然是0

最后,解释器进行添加、存储5x并记录它。

您可以通过在对象属性 getter/setter 中登录来检查此行为(在本例中,y.z,它反映了 的值x


Ber*_*rgi 11

你的声明x += await 5脱糖

const _temp = x;
await;
x = _temp + 5;
Run Code Online (Sandbox Code Playgroud)

_temp的卵巢值是0,如果你改变x过程中await(你的代码所做的)也没关系,它被分配5之后。


VLA*_*LAZ 9

这段代码非常复杂,因为它需要一些意外的来回异步跳转。让我们检查(接近)它实际上将如何执行,然后我将解释原因。我还更改了控制台日志以添加一个数字 - 使参考它们更容易,并更好地显示记录的内容:

let x = 0;                        // 1 declaring and assigning x

async function test() {           // 2 function declaration
    x += await 5;                 // 4/7 assigning x
    console.log('x1 :', x);       // 8 printing
}

test();                           // 3 invoking the function
x += 1;                           // 5 assigning x
console.log('x2 :', x);           // 6 printing
Run Code Online (Sandbox Code Playgroud)

所以,代码实际上并没有直接进行,这是肯定的。我们也有一件奇怪的4/7事情。这就是问题的全部。

首先,让我们澄清-异步函数没有真正严格asynchronious。如果使用await关键字,它们只会暂停执行并稍后恢复。没有它,它们会从上到下同步执行,一个接着一个表达式:

async function foo() {
  console.log("--one");
  console.log("--two");
}

console.log("start");
foo();
console.log("end");
Run Code Online (Sandbox Code Playgroud)

async function foo() {
  console.log("--one");
  await 0; //just satisfy await with an expression
  console.log("--two");
}

console.log("start");
foo();
console.log("end");
Run Code Online (Sandbox Code Playgroud)

因此,我们首先需要知道的是 usingawait将使函数的其余部分稍后执行。在给定的示例中,这意味着console.log('x1 :', x)将在其余同步代码之后执行。这是因为在当前事件循环完成后,任何 Promise 都会被解决。

所以,这就是为什么我们得到x2 : 1记录第一,为什么x2 : 5会记录第二然而不知其所以然后者价值5。逻辑上x += await 5应该是5...但这里是await关键字的第二个捕获- 它会暂停函数的执行,但在它已经运行之前的任何事情。x += await 5实际上将按以下方式处理

  1. 获取 的值x。在执行时,那是0
  2. await下一个表达式是5. 因此,功能现在暂停,稍后将恢复。
  3. 恢复功能。表达式解析为 5。
  4. 将 1. 中的值和 2/3 中的表达式相加: 0 + 5
  5. 将值从 4. 分配到 x

因此,该函数在读取xis后暂停,0并在已更改时恢复,但是,它不会重新读取x.

如果我们将 展开awaitPromise将执行的等效项,您将获得:

let x = 0;                        // 1 declaring and assigning x

async function test() {           // 2 function declaration
    const temp = x;               // 4 value read of x
    await 0; //fake await to pause for demo
    return new Promise((resolve) => {
      x = temp + 5;               // 7 assign to x
      console.log('x1 :', x);     // 8 printing
      resolve();
    });
}

test();                           // 3 invoking the function
x += 1;                           // 5 assigning x
console.log('x2 :', x);           // 6 printing
Run Code Online (Sandbox Code Playgroud)