Reputation: 986
I need to iterate an array in javascript with some values that will be used to call an asynchronous function that returns a promise. I can´t continue the next code section without all promises were completed.
In the following example, the function "processInvoices" has to resolve a promise until all promises inside were completed (assume that "confirmInvoice" is an asynchronous function that has different response times):
processInvoices(invoices)
{
return new promise((resolve,reject)=>
{
invoices.forEach(number=>
{
confirmInvoice(number)
.then(result=>{
if (!result)
{reject('Error confirming the invoice');}
});
});
resolve(true); // Resolving here doesn´t mean that all promises were completed!
});
}
init() // triggered at load..
{
let invoices = [2,4,8,16,31];
processInvoices(invoices)
.then(result=>
{
if (result) // It´s probable that the following message isn´t accurate:
console.log('All invoices were processed');
}).catch(reason=>{console.error(reason)});
}
With the code above, I can´t be sure that the "console.log (or any routine)" will be executed right away after all promises were completed.
UPDATE Promise.all(iterable) solves the problem :
processInvoices(invoices)
{
return new promise((resolve,reject)=>
{
var promisesArray = []; // store all promises here
invoices.forEach(number=>
{
promisesArray.push(confirmInvoice(number));
});
Promise.all(promisesArray).then (results=>{
// validate all results and reject if necessary...
if (validateResults(results)) {
// if all ok
resolve('All invoices were processed successfully');
}
else {
reject('Error processing one or more invoices'); // just for demo purposes
}
});
});
}
init() // triggered at load..
{
let invoices = [2,4,8,16,31];
processInvoices(invoices)
.then(result=>
{
console.log(result);
}).catch(reason=>{console.error(reason)});
}
Upvotes: 1
Views: 617
Reputation: 370679
forEach
runs synchronously. If you want to wait for all Promises to resolve before the full processInvoices
resolves, you should use Promise.all
instead; map
each invoice number to a Promise
and call Promise.all
on the resulting array of Promises. Also, your
if (!result) {resolve(false);}
sounds like it's an attempt to handle an error, in case there is no result - in this case, you should reject the Promise
instead of calling resolve
. Ideally, the failed confirmInvoice
call would result in a rejected Promise
, but if that's not something you can fix, throw an error if result
is falsey so you can handle it in a catch
in init
. For example:
function processInvoices(invoices) {
return Promise.all(
invoices.map(number => (
confirmInvoice(number)
.then(result => {
// This will result in the `Promise.all` rejecting:
if (!result) throw new Error('Failed to confirm invoice ' + number);
})
))
);
}
function init() {
const invoices = [2, 4, 8, 16, 31];
processInvoices(invoices)
.then(result => {
console.log('All invoices were processed');
})
.catch((err) => {
console.log('err: ' + err);
});
}
Upvotes: 1