Asynchronous for cycle in JavaScript
You can't mix synchronous and asynchronous in JavaScript if you block the script, you block the Browser.
You need to go the full event driven way here, luckily we can hide the ugly stuff away.
EDIT: Updated the code.
function asyncLoop(iterations, func, callback) {
var index = 0;
var done = false;
var loop = {
next: function() {
if (done) {
return;
}
if (index < iterations) {
index++;
func(loop);
} else {
done = true;
callback();
}
},
iteration: function() {
return index - 1;
},
break: function() {
done = true;
callback();
}
};
loop.next();
return loop;
}
This will provide us an asynchronous loop
, you can of course modify it even further to take for example a function to check the loop condition etc.
Now on to the test:
function someFunction(a, b, callback) {
console.log('Hey doing some stuff!');
callback();
}
asyncLoop(10, function(loop) {
someFunction(1, 2, function(result) {
// log the iteration
console.log(loop.iteration());
// Okay, for cycle could continue
loop.next();
})},
function(){console.log('cycle ended')}
);
And the output:
Hey doing some stuff!
0
Hey doing some stuff!
1
Hey doing some stuff!
2
Hey doing some stuff!
3
Hey doing some stuff!
4
Hey doing some stuff!
5
Hey doing some stuff!
6
Hey doing some stuff!
7
Hey doing some stuff!
8
Hey doing some stuff!
9
cycle ended
Asynchronous Process inside a javascript for loop
The for
loop runs immediately to completion while all your asynchronous operations are started. When they complete some time in the future and call their callbacks, the value of your loop index variable i
will be at its last value for all the callbacks.
This is because the for
loop does not wait for an asynchronous operation to complete before continuing on to the next iteration of the loop and because the async callbacks are called some time in the future. Thus, the loop completes its iterations and THEN the callbacks get called when those async operations finish. As such, the loop index is "done" and sitting at its final value for all the callbacks.
To work around this, you have to uniquely save the loop index separately for each callback. In Javascript, the way to do that is to capture it in a function closure. That can either be done be creating an inline function closure specifically for this purpose (first example shown below) or you can create an external function that you pass the index to and let it maintain the index uniquely for you (second example shown below).
As of 2016, if you have a fully up-to-spec ES6 implementation of Javascript, you can also use let
to define the for
loop variable and it will be uniquely defined for each iteration of the for
loop (third implementation below). But, note this is a late implementation feature in ES6 implementations so you have to make sure your execution environment supports that option.
Use .forEach() to iterate since it creates its own function closure
someArray.forEach(function(item, i) {
asynchronousProcess(function(item) {
console.log(i);
});
});
Create Your Own Function Closure Using an IIFE
var j = 10;
for (var i = 0; i < j; i++) {
(function(cntr) {
// here the value of i was passed into as the argument cntr
// and will be captured in this function closure so each
// iteration of the loop can have it's own value
asynchronousProcess(function() {
console.log(cntr);
});
})(i);
}
Create or Modify External Function and Pass it the Variable
If you can modify the asynchronousProcess()
function, then you could just pass the value in there and have the asynchronousProcess()
function the cntr back to the callback like this:
var j = 10;
for (var i = 0; i < j; i++) {
asynchronousProcess(i, function(cntr) {
console.log(cntr);
});
}
Use ES6 let
If you have a Javascript execution environment that fully supports ES6, you can use let
in your for
loop like this:
const j = 10;
for (let i = 0; i < j; i++) {
asynchronousProcess(function() {
console.log(i);
});
}
let
declared in a for
loop declaration like this will create a unique value of i
for each invocation of the loop (which is what you want).
Serializing with promises and async/await
If your async function returns a promise, and you want to serialize your async operations to run one after another instead of in parallel and you're running in a modern environment that supports async
and await
, then you have more options.
async function someFunction() {
const j = 10;
for (let i = 0; i < j; i++) {
// wait for the promise to resolve before advancing the for loop
await asynchronousProcess();
console.log(i);
}
}
This will make sure that only one call to asynchronousProcess()
is in flight at a time and the for
loop won't even advance until each one is done. This is different than the previous schemes that all ran your asynchronous operations in parallel so it depends entirely upon which design you want. Note: await
works with a promise so your function has to return a promise that is resolved/rejected when the asynchronous operation is complete. Also, note that in order to use await
, the containing function must be declared async
.
Run asynchronous operations in parallel and use Promise.all()
to collect results in order
function someFunction() {
let promises = [];
for (let i = 0; i < 10; i++) {
promises.push(asynchonousProcessThatReturnsPromise());
}
return Promise.all(promises);
}
someFunction().then(results => {
// array of results in order here
console.log(results);
}).catch(err => {
console.log(err);
});
Make for loop asynchronous in JavaScript
The problem in the above code is that it does not wait for getOne
to push data
into the output
array. In order to get the correct output
, you'll have to await the result of getOne
inside the for
loop.
async function getTotalQuestion(tag, question) {
var output = [];
for (let i = 0; i < question; i++) {
try {
var data = await getOne(tag);
output.push(data);
} catch (err) {
console.log(err);
}
}
return output;
}
synchronous and asynchronous loops in javascript
The for loop runs immediately to completion while all your asynchronous operations are started.
Well, here we have some nested loops. Notice, "BBB" always fires after.
for(let i=0; i<10; i++){
for(let i=0; i<10; i++){
for(let i=0; i<10; i++){
console.log("AA")
}
}
}
console.log('BBB')
now, look at this
for(let i=0; i<10; i++){
setTimeout(function() {console.log("AA")}, 2000)
}
console.log('BBB')
This is because of something called the "event loop". And the fact that with that setTimeout we are simulating an async operation. It could be an ajax call or some other async process.
Check this out: http://latentflip.com/loupe
This will really help you understand these sorts of async/sync loop topics.
updated to show how promises might work here (given comments below):
var stringValues = ['yeah', 'noooo', 'rush', 'RP'];
var P = function(val, idx){
return new Promise(resolve => setTimeout(() => resolve(val), 1000 * idx));
};
// We now have an array of promises waiting to be resolved.
// The Promise.all basically will resolve once ALL promises are
// resolved. Keep in mind, that if at any time something rejects
// it stops
// we iterator over our stringValues array mapping over the P function,
// passing in the value of our array.
var results = Promise.all(stringValues.map(P));
// once all are resolved, the ".then" now fires. It is here we would do
results.then(data =>
console.log(data) //["yeah", "noooo", "rush", "RP"]
);
let me know if I am not clear enough.
How to make for loop in JavaScript synchronous
You can't make a forEach
loop wait for an asynchronous function. It ignores the return value and calls the next function after the current function returned. You can use a for ... of ...
loop and await the promise. But await
can only be used inside an async
function (or inside an async
IIFE).
(async function() {
const splittedText = ["Hello", "World", "How", "Are", "You", "Today"];
let text ="";
for (const name of splittedText) {
if (name === "Are") {
await new Promise((resolve) => {
setTimeout(() => {
text = text + "ARE";
resolve();
}, 2000);
});
} else {
text += name + " ";
}
console.log(text)
};
})();
How can asynchronous method in loop executed in sequence?
Don't use a loop. Have the function call setTimeout()
to run itself again if it fails.
var i = 0;
function callGetDB() { getDB().then(db => { // use DB }).catch(() => { if (i++ < 3) { setTimeout(callGetDB, 200); } });}
Using async/await with a for in loop
I suspect that the problem is that Array.map
is async, so even though each one of the calls to save has await
in front of it, iterating the elements and calling the anonymous function inside the .map
is done in an async manner.
Try replacing uploadedFiles[key].map
with a simple for
loop and I believe that it'll fix the issue.
calling an async function inside for loop in JavaScript / node js
Just put async
in front of function test.
async function test(req, res) {…
If you want to await
a promise, then the function that's using await
needs to be async
function.
Related Topics
Angular 2 Sibling Component Communication
Differencebetween a User and a Guildmember in Discord.Js
How to Send a Variable Number of Arguments to a JavaScript Function
How to Perform a Dns Lookup (Hostname to Ip Address) Using Client-Side JavaScript
Empty Arrays Seem to Equal True and False at the Same Time
Jquery - Setting the Selected Value of a Select Control via Its Text Description
Javascript/Jquery: $(Window).Resize How to Fire After the Resize Is Completed
Jquery Xml Error ' No 'Access-Control-Allow-Origin' Header Is Present on the Requested Resource.'
How to Focus on a <Div> Using JavaScript Focus() Function
When to Use Es6 Class Based React Components VS. Functional Es6 React Components
What Is the Stability of the Array.Sort() Method in Different Browsers
Get Elements by Attribute When Queryselectorall Is Not Available Without Using Libraries
Declaring Variables Without Var Keyword
How to Check If Object Property Exists with a Variable Holding the Property Name
How to Sort an Array on Multiple Columns
Can You Get a Users Local Lan Ip Address via JavaScript