Asynchronous Process Inside a JavaScript For Loop

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);
});

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.

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.

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;
}

Wait for the for loop to complete and then return the value

Here is how to rewrite your specific code to accomplish what you're trying to accomplish:

const callbackFunc = (param1, param2, callback) => {
setTimeout(() => callback('Ok'), 1000)
}

let t = []
const test = ['1', '2'];
const promises = [];
for(let i in test) {

promises.push(new Promise(resolve => {
callbackFunc(2, test[i], result => {
resolve(result)
})
}))

}

Promise.all(promises)
.then(result => {
t = result;
console.log(t);
})

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 forEach loop

Sure the code does work, but I'm pretty sure it doesn't do what you expect it to do. It just fires off multiple asynchronous calls, but the printFiles function does immediately return after that.

Reading in sequence

If you want to read the files in sequence, you cannot use forEach indeed. Just use a modern for … of loop instead, in which await will work as expected:

async function printFiles () {
const files = await getFilePaths();

for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}

Reading in parallel

If you want to read the files in parallel, you cannot use forEach indeed. Each of the async callback function calls does return a promise, but you're throwing them away instead of awaiting them. Just use map instead, and you can await the array of promises that you'll get with Promise.all:

async function printFiles () {
const files = await getFilePaths();

await Promise.all(files.map(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
}));
}

async await, process one item of For loop at a time

Stackblitz demo

You can put an async inside the setTimeout:

myfunction() {
setTimeout(async () => {
for (const item of this.myarray) {
await this.wait(1000);
console.log(item);
}
});
}

wait(timer: number): Promise<void> {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), timer);
});
}


Related Topics



Leave a reply



Submit