英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论