Know When an Iteration Over Array with Async Method Is Finished

Whats the smartest / cleanest way to iterate async over arrays (or objs)?

Checkout the async library, it's made for control flow (async stuff) and it has a lot of methods for array stuff: each, filter, map. Check the documentation on github. Here's what you probably need:

each(arr, iterator, callback)

Applies an iterator function to each item in an array, in parallel. The iterator is called with an item from the list and a callback for when it has finished. If the iterator passes an error to this callback, the main callback for the each function is immediately called with the error.

eachSeries(arr, iterator, callback)

The same as each only the iterator is applied to each item in the array in series. The next iterator is only called once the current one has completed processing. This means the iterator functions will complete in order.

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

Iterate over async function

Promise is the best solution for handling asynchronous operations.

Promise.all(search_words.map(function(keyword) {  return new Promise(function(resolve, reject) {    request(base_url + keyword + "&l=", function(err, resp, body) {      if (err) {        return reject(err);      }      $ = cheerio.load(body);      resolve([keyword, $("#searchCount")[0].children[0].data.split(" ").reverse()[0]]);    });  });})).then(function(stats) {  console.log(stats);});

Returning array populated in loop by async function

The problem here is that you are using a plain for loop to traverse the animals array. Loops in NodeJS won't wait for the promise of the current iteration to resolve before moving onto the next, so the loop will finish before you have resolved your promises.

The best thing to do is to construct an array of promises you want to resolve, and then call Promise.all on that array.

async function getAnimalSounds(animals){
const promises = animals.map(a => asynchronously_fetch_sound(a))
const sounds = await Promise.all(promises)

return sounds
}

Or if you're happy to use the Bluebird library (docs here), you can do this:

const Bluebird = require('bluebird') // import Bluebird library

async function getAnimalSounds(animals){
const sounds = await Bluebird.map(animals, (a) => asynchronously_fetch_sound(a))

return sounds
}

Remember that since you have written an async function, you will need to wait for it to resolve before doing anything with its output; either by awaiting it or by calling .then(...).

Best way to wait for .forEach() to complete

If there is no asynchronous code inside the forEach, forEach is not asynchronous, for example in this code:

array.forEach(function(item){ 
//iterate on something
});
alert("Foreach DONE !");

you will see the alert after forEach finished.

Otherwise (You have something asynchronous inside), you can wrap the forEach loop in a Promise:

var bar = new Promise((resolve, reject) => {
foo.forEach((value, index, array) => {
console.log(value);
if (index === array.length -1) resolve();
});
});

bar.then(() => {
console.log('All done!');
});

Credit: @rolando-benjamin-vaz-ferreira

How to know if a promise inside a loop has ended?

You can solve this using Promise.all

Here is an example and here some useful documentation

the example is using map instead of a for loop to iterate over the array.

I haven't tested it but that should work

    Promise.all(
this.data_array.map((data) => {
console.log('Inserting object number ' + data.id);
return this.database.insertService(data);
})
)
.then((result) => console.log('done', result))
.catch((error) => console.log(error));

Return array after forEach loop (async) is finished

I don't understand why you loop over an array of IDs and use findOne to retrieve your objects, you could use something like this :

await this.Model.find({_id: {$in: modelIds}});

Also, instead of using forEach to push in an array, why don't you use map ?

I think you could do something like that :

async function recover(modelIds) {
return modelsIds.map(async (modelId, i) => {
const query = "..."; // your query with the id
return await this.Model.findOne(query);
})
}

And the surround your function call with try/catch to catch errors trhown during your function execution.

Asynchronously loop through array with key

You don't need a third-party library to accomplish this. Just use Promise.all() and Array.prototype.map().

const customerDetails = await Promise.all(
customers.map(customer => details(customer))
);

How to wait for a loop to finish in an async function before executing code in another async function?

If you change your method like this, that should work as expected:

async selectFile() {
await Promise.all(
this.Form.PostFiles.map(async(el, i) => {
const File = this.Form.PostFiles[i][0];
await this.uploadFile(File)
})
);
}


Related Topics



Leave a reply



Submit