Reputation: 485
I'm trying to find the lowest value ("best score") in 2 nested loops and then save the result after the loops are done. The following code seems to execute the final saving before the loops and not after them.
var bestScore = 300;
SaltEdge.getCustomerConnections(customerId, function(res1) {
Promise.all(res1.json.data.map(function(connection) {
return Promise.resolve()
.then(function() {
SaltEdge.getConnectionAccounts(connection.id, function(res2) {
if (res2.json.data) {
return Promise.all(res2.json.data.map(function(account) {
SaltEdge.get3MonthsIncome(connection.id, account.id, function(threeMonthsIncome) {
console.log('threeMonthsIncome', threeMonthsIncome);
var accountScore = SaltEdge.threeMonthsIncomeToScore(threeMonthsIncome);
console.log('account score', accountScore);
if (bestScore > accountScore) bestScore = accountScore;
console.log('best score', bestScore);
return bestScore;
});
}));
}
});
})
.then(function(result) {
return bestScore;
});
})
).then(function(res) {
console.log("i'm here" + bestScore, res);
if (bestScore < 300) {
console.log('--update score', bestScore);
Borrower.update(borrowerId, {salt_edge_score: bestScore}, function() {
done(new Response(200,{ updated: true }));
});
resolve(bestScore);
} else {
done(new Response(200,{ updated: true }));
}
});
});
Upvotes: 0
Views: 54
Reputation: 383
Aggregate all the Promises
into one array and use Promise.all()
with that array. Or have an array for the Promise.all()
of the inner loops and use another Promise.all()
to wait for them to all resolve
. Data structure of result [[...],[...],[...]]
.
So your call:
SaltEdge.getConnectionAccounts(connection.id, function(res2) {
if (res2.json.data) {
// is never used
return Promise.all(res2.json.data.map(function(account) {
runs into nothing because the Promise.all()
is never consumed because the function is in a .then()
block without a return
, followed by a .then()
that does nothing with a non existent value.
.then(function(result) {
return bestScore;
});
However since you reassign bestScore
in the callback of getConnectionAccounts
and this is a global you will have a side effect. So when the code above runs result
will be undefined and bestScore
unknown at the time since you reassign it in a callback. This means that all the Promises
from the Promise.all()
should resolve to garbage. (some value)
First order of business should be to get the reassignment, if (bestScore > accountScore) bestScore = accountScore;
, some where to the end of the chain. Make it conditional that all Promises have to resolve where Promise.all()
comes into play.
So contrary to the comment, you can insert asynchronous calls. BUT you have to return a Promise
from within the .then()
. Which means that either the callback has to resolve the returned Promise
or the called function has to return a Promise
of their own.
So does SaltEdge.getConnectionAccounts()
return a Promise
? If yes just return
the function call. If not use something like this:
return new Promise(function (resolve) {
SaltEdge.get3MonthsIncome(connection.id, account.id, function (threeMonthsIncome) {
console.log('threeMonthsIncome', threeMonthsIncome);
var accountScore = SaltEdge.threeMonthsIncomeToScore(threeMonthsIncome);
console.log('account score', accountScore);
if (bestScore > accountScore) bestScore = accountScore;
console.log('best score', bestScore);
resolve(bestScore);
});
})
To recap: We now have one promise for each score. We can feed all these Promises
to Promise.all()
at once. Just return an array from within your inner map()
and append a flat()
to your outer map()
.
Promise.all(
res1.json.data.map(
function (connection) {
// will return an array of promises
return SaltEdge.getConnectionAccounts(connection.id, function (res2) {
if (res2.json.data) {
return res2.json.data.map(function (account) {
// for better readability return added
return new Promise(function (resolve) {
SaltEdge.get3MonthsIncome(connection.id, account.id, function (threeMonthsIncome) {
console.log('threeMonthsIncome', threeMonthsIncome);
var accountScore = SaltEdge.threeMonthsIncomeToScore(threeMonthsIncome);
console.log('account score', accountScore);
// move this assignment
if (bestScore > accountScore) bestScore = accountScore;
console.log('best score', bestScore);
// resolving the promise inside the callback
resolve(bestScore);
});
})
})
}
});
}
// flat should get rid of the nested arrays
).flat()
) // use .then() for what to do with the array
The Promise.all()
will resolve to an array of all values of all calls. That's when you execute your code after 2 loops.
Upvotes: 1