TL;DR: You must await something
inside the async function to make it async.
var x = 0;
async function increment(x) {
await null; // <-- ADD THIS LINE
++x;
for (var i = 0; i < 10; i = i + 1);
console.log('value of x is: ' + x);
};
increment(x);
console.log('Out of the function');
Longer version:
First let's clarify one thing: JS runtime is an event-loop based single threaded runtime, this mean no concurrency/paralell code execution (unless you use Worker
or child_process
, that's another topic).
Now to the asynchronous part. The async-await
is just a handy syntax sugar that under the hood uses Promise
and Generator
, which in turn calls the event loop scheduling API provided by platfrom. In Node.js that API is process.nextTick
while in browser it's setImmediate
.
Through these APIs jobs are scheduled, buffered in the micro task queue, which will be tapped and executed by the event loop when idle. Thus the asynchronicity is in fact just scheduling.
With this knowledge let's come back to your case.
the async function
keyword does two things:
- it forces the function to return a
Promise
- it allows you to use the
await
keyword inside the function body
The actually scheduling part happens when you use the await
keyword. What it does, is to send a scheduled task to the micro task queue, which will always be a callback to a Promise.then()
call (in above example I send null
, it'll be auto-wrapped to be Promise.resolve(null)
), then pause the execution of current function, and wait until that promise resolves to continue execution of the rest.
So if you don't involve the event-loop scheduler by using await
keyword, then execution order will stay normal. Thus explain your case.
If you do undertand that async
function is just syntax sugar around Promise
, the next often seen misunderstanding is the execution order of new Promise(callback)
.
Case 1:
new Promise((resolve) => {
console.log('Bob comes first!')
resolve(null)
})
console.log('Alice comes first!')
Q: Who comes first?
A: Bob comes first.
Case 2:
new Promise((resolve) => {
resolve(null)
}).then(() => console.log('Bob comes first!'))
console.log('Alice comes first!')
Q: Who comes first?
A: This time Alice comes first.
Case 1 corresponds to your code. async
wrap the function into a promise's callback, but if you don't .then()
that callback is executed immediately, in normal order.
Case 2 corresponds to my await null
example. Which breaks up the function body into two parts, one inside new Promise(callback)
, the rest inside promise.then(callback)
. And the execution order of the latter is deferred.
increment(x)
will block last console.log(). I have put a link to MDN in my answer, may be its helpful to understand better. – Eucharistawait new Promise(setImmediate)
after every 1000th iteration or so, you'll give some other code (like your console.log) a chance to execute instead. – Wooldridge