Reputation: 189
I am learning Javascript. I am not used to asynchronous programming so having a hard time here. Here's my code:
var x = 0;
async function increment(x) {
++x;
for (var i = 0; i < 999999999; i = i + 1);
console.log('value of x is: ' + x);
};
increment(x);
console.log('Out of the function');
Expectation: 'Out of the function' gets printed instantly. 'value of x...' takes some time.
Reality: 'value of x...' gets printed first after a delay. Then 'Out of the function' is printed.
My question is, why isn't increment() being called asynchronously? Why is it blocking the last console.log?
Upvotes: 5
Views: 3498
Reputation: 19997
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:
Promise
await
keyword inside the function bodyThe 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.
Upvotes: 11
Reputation: 885
async
function has no await
, so function will run synchronously. If no await is provided, javascript does not throw any error.
From MDN
The body of an async function can be thought of as being split by zero or more await expressions. Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously
Convert it to something like below and you will see expected behaviour:
var x = 0;
async function increment(x) {
++x;
await setTimeout(function() {
for (var i = 0; i < 999999999; i = i + 1);
});
console.log('value of x is: ' + x);
};
increment(x);
console.log('Out of the function');
Upvotes: 2
Reputation: 529
That's because a function being asynchronous doesn't mean that it will always end last.
Wrap the async console.log in a setTimeout and you'll see the last console.log print first.
var x = 0;
async function increment(x) {
++x;
setTimeout(() => {
console.log('value of x is: ' + x);
}, 1000);
};
increment(x);
console.log('Out of the function');
Here is a Fiddle: https://jsfiddle.net/Ldjma3s8/
Upvotes: 0
Reputation: 159
The async keyword makes a function return a promise or 'thenable'. You need to await your promise increment.
Top level await is proposed but until we have it You would need your call to be within another async function or to use then.
https://github.com/tc39/proposal-top-level-await
var x = 0;
async function increment(x) {
++x;
for (var i = 0; i < 999999999; i = i + 1);
console.log('value of x is: ' + x);
};
async function waitThenLog() {
await increment(x);
console.log('Out of the function');
}
waitThenLog();
// or...
increment(x).then(() => console.log('Out of the function'));
Upvotes: 0
Reputation: 420
You should use await. From the official JS documentation: "The keyword await makes JavaScript wait until that promise settles and returns its result."
Also if it seems a bit strange but you can achieve your result with this:
const x = 0;
async function f(x) {
try {
++x;
for (i = 0; i < 999; i = i + 1)
console.log('value of x is:', await i);
} catch (e) {
console.log('error', e);
}
};
f(x);
console.log('Out');
Upvotes: 0
Reputation: 33
Because JavaScript is not multithreaded I suppose.
You could try with a JQuery approach (maybe) or learn about Web Workers The very first resource I found googlin' around: https://medium.com/techtrument/multithreading-javascript-46156179cf9a
Upvotes: 0