Take the following loop:
for(var i=0; i<100; ++i){
let result = await some_slow_async_function();
do_something_with_result();
}
Does await
block the loop? Or does the i
continue to be incremented while await
ing?
Is the order of do_something_with_result()
guaranteed sequential with regard to i
? Or does it depend on how fast the await
ed function is for each i
?
- Does
await
block the loop? Or does the i
continue to be incremented while await
ing?
"Block" is not the right word, but yes, i does not continue to be incremented while awaiting. Instead the execution jumps back to where the async
function was called, providing a promise as return value, continuing the rest of the code that follows after the function call, until the code stack has been emptied. Then when the awaiting is over, the state of the function is restored, and execution continues within that function. Whenever that function returns (completes), the corresponding promise -- that was returned earlier on -- is resolved.
- Is the order of
do_something_with_result()
guaranteed sequential with regard to i
? Or does it depend on how fast the await
ed function is for each i
?
The order is guaranteed. The code following the await
is also guaranteed to execute only after the call stack has been emptied, i.e. at least on or after the next microtask can execute.
See how the output is in this snippet. Note especially where it says "after calling test":
async function test() {
for (let i = 0; i < 2; i++) {
console.log('Before await for ', i);
let result = await Promise.resolve(i);
console.log('After await. Value is ', result);
}
}
test().then(_ => console.log('After test() resolved'));
console.log('After calling test');
As @realbart says, it does block the loop, which then will make the calls sequential.
If you want to trigger a ton of awaitable operations and then handle them all together, you could do something like this:
const promisesToAwait = [];
for (let i = 0; i < 100; i++) {
promisesToAwait.push(fetchDataForId(i));
}
const responses = await Promise.all(promisesToAwait);
You can test async/await inside a "FOR LOOP" like this:
(async () => {
for (let i = 0; i < 100; i++) {
await delay();
console.log(i);
}
})();
function delay() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 100);
});
}
Does await block the loop? Or does the i
continue to be incremented while awaiting?
No, await won't block the looping. Yes, i
continues to be incremented while looping.
Is the order of do_something_with_result() guaranteed sequential with regard to i
? Or does it depend on how fast the awaited function is for each i
?
Order of do_something_with_result()
is guaranteed sequentially but not with regards to i
. It depends on how fast the awaited function runs.
All calls to some_slow_async_function()
are batched, i.e., if do_something_with_result()
was a console
then we will see it printed the number of times the loop runs. And then sequentially, after this, all the await calls will be executed.
To better understand you can run below code snippet:
async function someFunction(){
for (let i=0;i<5;i++){
await callAPI();
console.log('After', i, 'th API call');
}
console.log("All API got executed");
}
function callAPI(){
setTimeout(()=>{
console.log("I was called at: "+new Date().getTime())}, 1000);
}
someFunction();
One can clearly see how line console.log('After', i, 'th API call');
gets printed first for entire stretch of the for loop and then at the end when all code is executed we get results from callAPI()
.
So if lines after await were dependent on result obtained from await calls then they will not work as expected.
To conclude, await
in for-loop
does not ensure successful operation on result obtained from await calls which might take some time to finish.
In node, if one uses neo-async
library with waterfall
, one can achieve this.