Promise.all无法与Mongo查询数组一起正常工作

时间:2018-08-25 16:37:32

标签: javascript

我有一个路由数组( recommendedRoutes ),我需要对其进行迭代并从MongoDB获取该路由的所有者( User )。从数据库中获取路由后,我会将附加的 userObj 附加到路由。

let promiseArr = [];

for (let index = 0; index < obj.recommendedRoutes.length; index++) {
    console.log("Pushed: "+index);
    promiseArr.push( 
        User.find({
            _id : obj.recommendedRoutes[index].userId,
        },(err,data) => {
            if(err) {
                console.log("Database error: " + err);
            }else{
                obj.recommendedRoutes[index].userObj = data[0].toObject();
                console.log("Completed: "+index);
            }
        })
    );    
}

在将 userObj 附加到数组中的所有路由对象之后,我需要返回此数组。因此,我将所有查询推送到 promiseArr 中,并使用Promise.all来确保仅在所有promise解决后才返回数组。

Promise.all(promiseArr)
.then(() => {

    console.log("Return Object");
    return res.status(200).send({
        success: true,
        message: "Successfully retrieved Recommended Routes/Carpools",
        obj: obj,
    });

})
.catch((e) => {
    throw "There was an error: "+e;
});

问题在于它有时会在promise解决之前返回数组。 预期输出:

Pushed: 0
Pushed: 1
Completed: 1
Completed: 0
Return Object

偶尔会发生什么:

Pushed: 0
Pushed: 1
Completed: 1
Return Object
Completed: 0

2 个答案:

答案 0 :(得分:0)

您不是在使用Promises,而是在使用回调...

这是正确的方法:

import tkinter as tk

class Character:
    def __init__(self, availablePoints):
        self.name = ""
        self.experience = 0
        self.level = 0
        self.gold = 0
        self.stat1 = 0
        self.stat2 = 0
        self.stat3 = 0
        self.stat4 = 0
        self.stat5 = 0
        self.availablePoints = availablePoints

class Game:
    def __init__(self, parent):
        self.myParent = parent
        self.myGame = tk.Frame(parent)
        self.myGame.grid()

        self.statsFrame = tk.Frame(self.myGame)
        self.statsFrame.grid()

        self.make_stat("Strength:", "stat1", 1, 1)

    def make_stat(self, text, stat, column, row):
        label = tk.Label(self.statsFrame, text=text)
        label.grid(column=column, row=row)

        amount = tk.Label(self.statsFrame, text=getattr(myChar, stat))
        amount.grid(column=(column+1), row=row)

        def update():
            if myChar.availablePoints >= 1:
                v = getattr(myChar, stat) + 1
                setattr(myChar, stat, v)
                myChar.availablePoints -= 1
                amount["text"] = v

        button = tk.Button(self.statsFrame, text="+", command=update)
        button.grid(column=(column+2), row=row)

myChar = Character(5)

root = tk.Tk()
myapp = Game(root)
root.mainloop()

答案 1 :(得分:0)

以更好的代码的名义,在开始更新recommendedRoutes之前,请等待所有答复。

使用当前的方法,您将在相应的响应到达时立即更新各个路线对象。在这些响应之间,您的阵列处于过渡状态。它不再包含(全部)旧数据,但不再包含来自服务器的(全部)新数据。上帝禁止您的任何请求都可能失败。由于每个更新(阵列中的每个项目)都是单独处理的,因此您如何回滚或前进。

@ m1ch4ls答案也有同样的问题,它只是解决了回调的使用,而不是承诺。

恕我直言。一些更好的代码

Promise.all(
    //make all the individual requests
    obj.recommendedRoutes.map(
        route => User.find({ _id: route.userId })
    )
).then(
    //and only whe they ALL have returned successfully 
    data => {
        //update all your state at once
        data.forEach((item, index) => {
            obj.recommendedRoutes[index].userObj = item.toObject()
        });
    },
    err => {
        //or return an error-message if anyone fails
        console.log("Database error: " + err);
    }
);