let x = 0; async function test() { x += await 5; console.log('x :', x); } test(); x += 1; console.log('x :', x); The values of x logged are 1 and 5. My question is: why is the value of x 5 on second log?
If the test is executed after x += 1 (since it is an async function) then the value of x is 1 by the time test is executed, so x += await 5 should make the value of x 6.
Inside an async function, you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.
An async function (or AyncFunction) in the context of Async is an asynchronous function with a variable number of parameters where the final parameter is a callback.
Using async functions with event handlers is problematic, because it can lead to an unhandled rejection in case of a thrown exception: const ee = new EventEmitter(); ee. on('something', async (value) => { throw new Error('kaboom'); });
The async function helps to write promise-based code asynchronously via the event-loop. Async functions will always return a value. Await function can be used inside the asynchronous function to wait for the promise. This forces the code to wait until the promise returns a result.
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 are 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 is an already resolved promise (or as in here, not a promise at all - these will be converted to resolved promises by await), so the following will return 1 (from the main script) and 6 (from the function), as you expected:
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) (unless x is an expression with side-effects, which isn't the case here). 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 evaluates the expression inside await (5), turns it into a resolved promise, and starts waiting for it.
The code after the function call starts to run, and modifies the value of x (from 0 to 1), then logs it.
x is now 1.
Then, after the main script finishes, the interpreter goes back to the paused test function, and continues evaluating the line, which, with the await out of the way, looks like this:
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); console.log(new Error().stack.replace('Error', 'Stacktrace')); //Log stacktrace using an Error object return x; }, set z(value) { console.log('set x =', value); console.log(new Error().stack.replace('Error', 'Stacktrace')); //Log stacktrace using an Error object 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); console.log('end of main script') /* Output: inside async function get x : 0 <-------------- async fn reads Stacktrace at Object.get z [as z] (https://stacksnippets.net/js:19:17) at test (https://stacksnippets.net/js:31:3) <-- async fn is synchronous here at https://stacksnippets.net/js:35:1 <--------- (main script is still in the stack) main script get x : 0 Stacktrace at Object.get z [as z] (https://stacksnippets.net/js:19:17) at https://stacksnippets.net/js:37:1 set x = 1 Stacktrace at Object.set z [as z] (https://stacksnippets.net/js:24:17) at https://stacksnippets.net/js:37:5 x : 1 end of main script set x = 5 <-------------- async fn writes Stacktrace at Object.set z [as z] (https://stacksnippets.net/js:24:17) at test (https://stacksnippets.net/js:31:7) <-- async fn is asynchronous (main script is no longer in the stack) x : 5 <------------------ async fn logs */ /* Just to make console fill the available space */ .as-console-wrapper { max-height: 100% !important; } Your statement x += await 5 desugars to
const _temp = x; const _gain = await 5; x = _temp + _gain; The _temporary value is 0, and if you change x during the await (which your code does) it doesn't matter, it gets assigned 5 afterwards.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With