Async function with +=

后端 未结 5 2033
礼貌的吻别
礼貌的吻别 2020-12-24 11:39



        
5条回答
  •  不思量自难忘°
    2020-12-24 12:12

    TL;DR: Because += reads x before, but writes it after it has changed, due to the await keyword in its second operand (right-hand side).


    async functions run synchronously when they called until the first await statement.

    So, if you remove await, it behaves like a normal function (with the exception that it still returns a Promise).

    In that case, you get 5 (from the function) and 6 (from the main script) in the console:

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


    The first await stops synchronous running, even if its argument available synchronously, so the following will return 1 (from the main script) and 6 (from the function), as you expect:

    let x = 0;
    
    async function test() {
      // Enter asynchrony
      await 0;
    
      x += 5;
      console.log('x :', x);
    }
    
    test();
    x += 1;
    console.log('x :', x);


    However, your case is a bit more complicated.

    You've put await inside an expression, that uses +=.

    You probably know, that in JS x += y is identical to x = (x + y). I'll use the latter form for better understanding:

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

    When the interpreter reaches this line...

    x = (x + await 5);
    

    ...it starts evaluating it, substitutes x, so it turns to...

    x = (0 + await 5);
    

    ...then, it reaches the await and stops.

    The code after the function call starts to run, and modifies the value of x, then logs it.

    x is now 1.

    Then, after the main script exits, the interpreter goes back to the paused test function, and continues evaluating that line:

    x = (0 + 5);
    

    And, since the value of x is already substituted, it remains 0.

    Finally, the interpreter does the addition, stores 5 to x, and logs it.

    You can check this behaviour by logging inside an object property getter/setter (in this example, y.z, which reflects the value of x:

    let x = 0;
    const y = {
      get z() {
        console.log('get x :', x);
        return x;
      },
      set z(value) {
        console.log('set x =', value);
        x = value;
      }
    };
    
    async function test() {
      console.log('inside async function');
      y.z += await 5;
      console.log('x :', x);
    }
    
    test();
    console.log('main script');
    y.z += 1;
    console.log('x :', x);
    
    /* Output:
    
    inside async function
    get x : 0   <-- async fn reads
    main script
    get x : 0
    set x = 1
    x : 1
    set x = 5   <-- async fn writes
    x : 5       <-- async fn logs
    
    */
    /* Just to make console fill the available space */
    .as-console-wrapper {
      max-height: 100% !important;
    }

提交回复
热议问题