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;
}