I have a loop which calls a method that does stuff asynchronously. This loop can call the method many times. After this loop, I have another loop that needs to be executed only when all the asynchronous stuff is done.
So this illustrates what I want:
for (i = 0; i < 5; i++) { doSomeAsyncStuff();
}
for (i = 0; i < 5; i++) { doSomeStuffOnlyWhenTheAsyncStuffIsFinish();
}I'm not very familiar with promises, so could anyone help me to achieve this?
This is how my doSomeAsyncStuff() behaves:
function doSomeAsyncStuff() { var editor = generateCKEditor(); editor.on('instanceReady', function(evt) { doSomeStuff(); // There should be the resolve() of the promises I think. })
}Maybe I have to do something like this:
function doSomeAsyncStuff() { var editor = generateCKEditor(); return new Promise(function(resolve,refuse) { editor.on('instanceReady', function(evt) { doSomeStuff(); resolve(true); }); });
}But I'm not sure of the syntax.
46 Answers
You can use Promise.all (spec, MDN) for that: It accepts a bunch of individual promises and gives you back a single promise that is resolved when all of the ones you gave it are resolved, or rejected when any of them is rejected.
So if you make doSomeAsyncStuff return a promise, then:
const promises = [];
// ^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−− use `const` or `let`, not `var` for (let i = 0; i < 5; i++) {
// ^^^−−−−−−−−−−−−−−−−−−−−−−−− added missing declaration promises.push(doSomeAsyncStuff()); } Promise.all(promises) .then(() => { for (let i = 0; i < 5; i++) {
// ^^^−−−−−−−−−−−−−−−− added missing declaration doSomeStuffOnlyWhenTheAsyncStuffIsFinish(); } }) .catch((e) => { // handle errors here });MDN has an article on promises here. I also cover promsies in detail in Chapter 8 of my book JavaScript: The New Toys, links in my profile if you're interested.
Here's an example:
function doSomethingAsync(value) { return new Promise((resolve) => { setTimeout(() => { console.log("Resolving " + value); resolve(value); }, Math.floor(Math.random() * 1000)); }); } function test() { const promises = []; for (let i = 0; i < 5; ++i) { promises.push(doSomethingAsync(i)); } Promise.all(promises) .then((results) => { console.log("All done", results); }) .catch((e) => { // Handle errors here }); } test();Sample output (because of the Math.random, what finishes first may vary):
Resolving 3 Resolving 2 Resolving 1 Resolving 4 Resolving 0 All done [0,1,2,3,4]9
A reusable function works nicely for this pattern:
function awaitAll(count, asyncFn) { const promises = []; for (i = 0; i < count; ++i) { promises.push(asyncFn()); } return Promise.all(promises);
}OP example:
awaitAll(5, doSomeAsyncStuff) .then(results => console.log('doSomeStuffOnlyWhenTheAsyncStuffIsFinished', results)) .catch(e => console.error(e));A related pattern, is iterating over an array and performing an async operation on each item:
function awaitAll(list, asyncFn) { const promises = []; list.forEach(x => { promises.push(asyncFn(x)); }); return Promise.all(promises);
}Example:
const books = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }];
function doSomeAsyncStuffWith(book) { return Promise.resolve(book.name);
}
awaitAll(books, doSomeAsyncStuffWith) .then(results => console.log('doSomeStuffOnlyWhenTheAsyncStuffIsFinished', results)) .catch(e => console.error(e)); 1 const doSomeAsyncStuff = async (funcs) => { const allPromises = funcs.map(func => func()); return await Promise.all(allPromises);
}
doSomeAsyncStuff([ () => new Promise(resolve => setTimeout(() => resolve(), 100)), () => new Promise(resolve => setTimeout(() => resolve(), 100)), () => new Promise(resolve => setTimeout(() => resolve(), 100)), () => new Promise(resolve => setTimeout(() => resolve(), 100)), () => new Promise(resolve => setTimeout(() => resolve(), 100)),
]); /*** Worst way ***/
for(i=0;i<10000;i++){ let data = await axios.get( "" ) //do the statements and operations //that are dependant on data
}
//Your final statements and operations
//That will be performed when the loop ends
//=> this approach will perform very slow as all the api call
// will happen in series
/*** One of the Best way ***/
const yourAsyncFunction = async (anyParams) => { let data = await axios.get( "" ) //all you statements and operations here //that are dependant on data
}
var promises = []
for(i=0;i<10000;i++){ promises.push(yourAsyncFunction(i))
}
await Promise.all(promises)
//Your final statement / operations
//that will run once the loop ends
//=> this approach will perform very fast as all the api call
// will happen in parallal Here is code that I wrote for myself in order to understand the answers stated here. I have mongoose queries in a for loop, so I put here the asyncFunction to take its place. Hope it helps anyone. You can run this script in node or any of many Javascript runtimes.
let asyncFunction = function(value, callback)
{ setTimeout(function(){console.log(value); callback();}, 1000);
}
// a sample function run without promises
asyncFunction(10, function() { console.log("I'm back 10"); }
);
//here we use promises
let promisesArray = [];
let p = new Promise(function(resolve)
{ asyncFunction(20, function() { console.log("I'm back 20"); resolve(20); } );
});
promisesArray.push(p);
for(let i = 30; i < 80; i += 10)
{ let p = new Promise(function(resolve) { asyncFunction(i, function() { console.log("I'm back " + i); resolve(i); } ); }); promisesArray.push(p);
}
// We use Promise.all to execute code after all promises are done.
Promise.all(promisesArray).then( function() { console.log("all promises resolved!"); }
) Here's an elegant solution for you if you want to do the same thing multiple times:
await Promise.all(new Array(10).fill(0).map(() => asyncFn()));This creates an array with 10 items, fills it with zeros and then maps it to an array of promises.