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 _temp
orary 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