Async for Loop in Node.Js

async for loop in node.js

I've reduced your code sample to the following lines to make it easier to understand the explanation of the concept.

var results = [];
var config = JSON.parse(queries);
for (var key in config) {
var query = config[key].query;
search(query, function(result) {
results.push(result);
});
}
res.writeHead( ... );
res.end(results);

The problem with the previous code is that the search function is asynchronous, so when the loop has ended, none of the callback functions have been called. Consequently, the list of results is empty.

To fix the problem, you have to put the code after the loop in the callback function.

    search(query, function(result) {
results.push(result);
// Put res.writeHead( ... ) and res.end(results) here
});

However, since the callback function is called multiple times (once for every iteration), you need to somehow know that all callbacks have been called. To do that, you need to count the number of callbacks, and check whether the number is equal to the number of asynchronous function calls.

To get a list of all keys, use Object.keys. Then, to iterate through this list, I use .forEach (you can also use for (var i = 0, key = keys[i]; i < keys.length; ++i) { .. }, but that could give problems, see JavaScript closure inside loops – simple practical example).

Here's a complete example:

var results = [];
var config = JSON.parse(queries);
var onComplete = function() {
res.writeHead( ... );
res.end(results);
};
var keys = Object.keys(config);
var tasksToGo = keys.length;
if (tasksToGo === 0) {
onComplete();
} else {
// There is at least one element, so the callback will be called.
keys.forEach(function(key) {
var query = config[key].query;
search(query, function(result) {
results.push(result);
if (--tasksToGo === 0) {
// No tasks left, good to go
onComplete();
}
});
});
}

Note: The asynchronous code in the previous example are executed in parallel. If the functions need to be called in a specific order, then you can use recursion to get the desired effect:

var results = [];
var config = JSON.parse(queries);
var keys = Object.keys(config);
(function next(index) {
if (index === keys.length) { // No items left
res.writeHead( ... );
res.end(results);
return;
}
var key = keys[index];
var query = config[key].query;
search(query, function(result) {
results.push(result);
next(index + 1);
});
})(0);

What I've shown are the concepts, you could use one of the many (third-party) NodeJS modules in your implementation, such as async.

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.

NodeJS: async loop that waits between iterations

const myArray = ['a','b','c','d'];
async function wait(ms) { // comment 3 return new Promise(resolve => setTimeout(resolve, ms));}
async function doSomething() { await myArray.reduce(async (promise, item) => { await promise; // comment 2 await wait(1000);
// here we could await something else that is async like DB call document.getElementById('results').append(`${item} `); }, Promise.resolve()); // comment 1}

setTimeout(() => doSomething(), 1000);
<div id="results">Starting in 1 second <br/></div>

How to properly use await/async with for loops in node js

This answer doesn't fix your code, but just to clear up any miscomprehension:

fs.stat(path, callback); // fs.stat is always asynchronous.
// callback is not (normally),
// but callback will run sometime in the future.

The only way to await functions like fs.stat which is using callbacks instead of promises, is to make the promise yourself.

function promiseStat( path ){
return new Promise( ( resolve, reject ) => {
fs.stat( path, ( err, stat ) => {
if( err ) reject( err );
else resolve( stat );
};
});
}

Now we can:

const stat = await promiseStat( path );

how to use await/async in for loop

Simple typo, you are not calling the resolve function. You need to actually invoke it, otherwise your promise will never resolve and your code will never continue (note the addition of parentheses after resolve in your print function):

async function print() {
for (let n of arr) {
await new Promise(
resolve => {
setTimeout(() => {
console.log(n);
resolve(); // invoke the function
}, 1000)
}
);
}
}

Also, each console.log call prints on a new line so the output won't be exactly what you expect, instead it'll be more like:

>>>>> START
1
2
3
>>>>> END

Waiting for async function in for loop

You do it in a wrong way, you don't wait anything in you for-loop.

return await Promise.all(all_vacations); does not work, because all_vacations is not a Promise array.

In your case, we have many way to do this, my way just is a example: Create a array to store all promises what have been created in your for-loop, then wait until the all promises finished by Promise.all syntax.

async function loop_through_vacations(vacations_incomplete, pool) {
var all_vacations = [];
var promises = []; // store all promise
for (var vacation_data of vacations_incomplete) {
var vacation_at_index = new vac.Vacation(vacation_data.id, vacation_data.destination, vacation_data.description, vacation_data.attendee_creator_id);
promises.push( // push your promise to a store - promises
vacation_at_index.find_creator(pool)
.then(() => {
all_vacations.push(vacation_at_index)
})
.catch((err) => {
console.log(err); // Skip error???
})
);
}
await Promise.all(promises); // wait until all promises finished
return all_vacations;
}

I don't know why you use async/await mix with callback style, I recommend async/await for any case:

Mix style:

function get_all_vacations(callback) {
var sql_vacations_query = "SELECT * FROM vacation";
pool.query(sql_vacations_query, async function(error, vacations_query_result) { // async
if (error) {
console.log("error in vacations query " + error);
return callback(error); // return to break process
}
var all_complete = await loop_through_vacations(vacations_query_result.rows, pool); // await
callback(null, all_complete);
});
}

async/await:

async function get_all_vacations() {
var sql_vacations_query = "SELECT * FROM vacation";
var rows = await new Promise((resolve, reject) => {
pool.query(sql_vacations_query, function(error, vacations_query_result) {
if (error) {
console.log("error in vacations query " + error);
return reject(error);
}
resolve(vacations_query_result.rows);
});
})
var all_complete = await loop_through_vacations(rows, pool); // await
return all_complete;
}

NodeJS wait for loop to finish (async / await / promisse)

You can do something like this:

function run() {
getInfo(...).then(() => {
setTimeout(run, 5000)
});
}

run().catch(err => {
console.log(err);
});

Or, I prefer using a promise version of setTimeout(). I the latest version of nodejs, you can use timersPromises.setTimeout() and it returns a promise:

import { setTimeout } from 'timers/promises';

async function run() {
await getInfo(...)
await setTimeout(5000);
return run();
}

run().catch(err => {
console.log(err);
});

Or, use a while loop with await:

import { setTimeout } from 'timers/promises';

async function run() {
while (true) {
await getInfo(...)
await setTimeout(5000);
}
}

run().catch(err => {
console.log(err);
});


Related Topics



Leave a reply



Submit