Below is a snippet from my javascript function. It's taking an arbitrary size array of objects travelDataGroups[{}] and iterating over it. Inside of the forEach() I have to call newUser(), which is async, and inside that I have to call loadOtherUsersPayRate() which is asynchronous.
After all the callbacks are resolved and the loop is done, I need to call makeWorkbookDownloadObj(), the catch is it can only be called once, else it corrupts the object.
myGroupWorkReportObj.travelDataGroups.forEach( travelerData =>
{
// add the pay rate for this user
const someUser = new User( travelerData.employeeId , function()
{
// add the pay rate for this user and project
const projectId = travelerData.projectId;
const payRate = loadOtherUsersPayRate( projectId, someUser )
.then( payRate =>
{
//create a row of travel data
const rowOfData = [
travelerData.firstName,
travelerData.lastName,
travelerData.travelHours,
financial( payRate.pay / 2 )
];
// push the row of data into the sheet
thisSheet.data.push( rowOfData );
// need to only call this once, when all callbacks are resolved
// makeWorkbookDownloadObj( workbookObj );
});
});
EDIT final working code:
const userPromises = [];
// wrap the User class in a Promise
var aUserPromise = ( travelerData ) =>
{
return new Promise( ( resolve, reject ) =>
// new User( employeeId, ( someUser ) =>
{
// return new Promise( ( resolve, reject ) =>
new User( travelerData.employeeId, ( someUserObj ) =>
{
// add the pay rate for this user and project
loadOtherUsersPayRate( travelerData.projectId, someUserObj )
.then( payRate =>
{
//create a row of travel data
const rowOfData = [
travelerData.firstName,
travelerData.lastName,
travelerData.travelHours,
financial( payRate.pay / 2 )
];
// push the row of data into the sheet
thisSheet.data.push( rowOfData );
resolve();
} );
} );
} );
};
myGroupWorkReportObj.travelDataGroups.forEach(( travelerData ) =>
{
// add the pay rate for this user
** this line would push the function, but not execute it (until later),
** so did in two lines as below
// userPromises.push(aUserPromise(travelerData));
var myRun = aUserPromise( travelerData );
userPromises.push( myRun );
});
Promise.allSettled( userPromises )
.then( ()=>
{
makeWorkbookDownloadObj( workbookObj )
});
I do this - use Promise.all - see here for docs:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Note the code below is untested but you can see how to do it:
const promises = []
promises.push(
loadOtherUsersPayRate( projectId, someUser )
.then( payRate =>
{
//create a row of travel data
const rowOfData = [
travelerData.firstName,
travelerData.lastName,
travelerData.travelHours,
financial( payRate.pay / 2 )
];
// push the row of data into the sheet
thisSheet.data.push( rowOfData );
// need to only call this once, when all callbacks are resolved
// makeWorkbookDownloadObj( workbookObj );
});
)
Promise.all(promises).then( ()= > makeWorkbookDownloadObj( workbookObj ));
Following your comment and using Promise ALL and same pattern used by previous answer:
const userPromises = [];
myGroupWorkReportObj.travelDataGroups.forEach( travelerData =>
{
// add the pay rate for this user
userPromises.push(new User( travelerData.employeeId , function()
{
// add the pay rate for this user and project
const projectId = travelerData.projectId;
const payRate = loadOtherUsersPayRate( projectId, someUser )
.then( payRate =>
{
//create a row of travel data
const rowOfData = [
travelerData.firstName,
travelerData.lastName,
travelerData.travelHours,
financial( payRate.pay / 2 )
];
// push the row of data into the sheet
thisSheet.data.push( rowOfData );
})
}));
});
Promise.all(userPromises).then(() => {
makeWorkbookDownloadObj( workbookObj );
});
The true promises needing go be resolved are the users creation.