在Node中,在两个嵌套循环后执行代码的方法如下:

huangapple go评论77阅读模式
英文:

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(&#39;threeMonthsIncome&#39;, threeMonthsIncome);
                                var accountScore = SaltEdge.threeMonthsIncomeToScore(threeMonthsIncome);
                                console.log(&#39;account score&#39;, accountScore);
                                if (bestScore &gt; accountScore) bestScore = accountScore;

                                console.log(&#39;best score&#39;, bestScore);
                                return bestScore;
                            });
                        }));
                    }
                });

            })
            .then(function(result) {
                return bestScore;
            });
    })
    ).then(function(res) {
        console.log(&quot;i&#39;m here&quot; + bestScore, res);
        if (bestScore &lt; 300) {
            console.log(&#39;--update score&#39;, 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 &gt; 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 &gt; 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 &gt; 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 &gt; 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(&#39;threeMonthsIncome&#39;, threeMonthsIncome);
        var accountScore = SaltEdge.threeMonthsIncomeToScore(threeMonthsIncome);
             console.log(&#39;account score&#39;, accountScore);
             if (bestScore &gt; accountScore) bestScore = accountScore;

             console.log(&#39;best score&#39;, 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(&#39;threeMonthsIncome&#39;, threeMonthsIncome);
                                var accountScore = SaltEdge.threeMonthsIncomeToScore(threeMonthsIncome);
                                console.log(&#39;account score&#39;, accountScore);

                                // move this assignment
                                if (bestScore &gt; accountScore) bestScore = accountScore;

                                console.log(&#39;best score&#39;, 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.

huangapple
  • 本文由 发表于 2020年1月6日 02:36:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/59603018.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定