英文:
How to execute code after 2 nested loops in node
问题
我正在尝试在两个嵌套循环中找到最低值(“最佳分数”),然后在循环完成后保存结果。以下代码似乎在循环之前执行最终保存,而不是在它们之后。
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);
});
}));
}
});
});
})
).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 }));
}
});
});
希望这可以帮助您解决问题。
英文:
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 }));
}
});
});
答案1
得分: 1
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;, somewhere 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.
英文:
Short version:
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 [[...],[...],[...]].
Why it fails:
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)
How to for your case:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论