如何使用承诺和循环猫鼬集合

时间:2019-05-22 14:28:30

标签: javascript node.js mongoose

我正在网站内聊天。为了存储数据,我使用聊天,用户,消息集合。

我希望结果在包含以下内容的数组中:

[{
  username (another one, not me)
  last update
  last message
}] 

在聊天模型中,我只有chatidarray of two members,因此我需要遍历User集合以使用user name来获得user id。我想将所有名称保存在数组中(将来,我也想遍历消息以获取每个chatid的最新消息)。问题是,当我返回chatsList时,它为空。我认为我需要某种方式来使用Promise,但是我不确定它应该如何工作。

Chat.find({ members: userId })
    .then(chats => {
      let chatsList = [];
      chats.forEach((chat, i) => {
        let guestId = chat.members[1 - chat.members.indexOf(userId)];
        User.findOne({ _id: guestId })
          .then(guest => {
            let chatObj = {};
            name = guest.name;
            chatsList.push(name);
            console.log("chatsList", chatsList)
          })
          .catch(err => console.log("guest err =>", err))
      })
      return res.json(chatsList)
    })
    .catch(err => {
      errors.books = "There are no chats for this user";
      res.status(400).json(errors);
    })

3 个答案:

答案 0 :(得分:1)

实际上,Promise.all是您要寻找的:

Chat.find({ members: userId })
    .then(chats => {
        let userPromises = [];
        chats.forEach((chat, i) => {
            let guestId = chat.members[1 - chat.members.indexOf(userId)];
            userPromises.push(User.findOne({ _id: guestId }));
        });
        return Promise.all(userPromises).then(guests => {
            let chatsList = [];
            guests.forEach(guest => {
                chatsList.push(guest.name);
            });
            return res.json(chatsList);
        });
    });
});

尽管最好使用ID列表($in查询)对DB进行一次调用。像这样:

Chat.find({ members: userId })
    .then(chats => {
        let ids = [];
        chats.forEach((chat, i) => {
            let guestId = chat.members[1 - chat.members.indexOf(userId)];
            ids.push(guestId);
        });
        return User.find({_id: {$in: ids}}).then(guests => {
            let chatsList = [];
            guests.forEach(guest => {
                chatsList.push(guest.name);
            });
            return res.json(chatsList);
        });
    });
});

您可能想另外验证每个id是否都有对应的guest

答案 1 :(得分:0)

您可以尝试以下方法:

Chat.find({ members: userId }).then(chats => {
    let guestHashMap = {};


    chats.forEach(chat => {
        let guestId = chat.members.filter(id => id != userId)[0];
        // depending on if your ID is of type ObjectId('asdada')
        // change it to guestHashMap[guestId.toString()] = true;
        guestHashMap[guestId] = true;
    })
    return Promise.all(
        // it is going to return unique guests
        Object.keys(guestHashMap)
            .map(guestId => {
                // depending on if your ID is of type ObjectId('asdada')
                // change it to User.findOne({ _id: guestHashMap[guestId] })
                return User.findOne({ _id: guestId })
            }))
})
.then(chats => {
    console.log(chats.map(chat => chat.name))
    res.json(chats.map(chat => chat.name))
})
.catch(err => {
    errors.books = "There are no chats for this user";
    res.status(400).json(errors);
})

答案 2 :(得分:0)

您遇到了并发问题。例如,运行chats.forEach,在forEach内部运行User.findOne().thenreturn语句在User.findOne()兑现之前就已经执行了。这就是为什么您的列表为空。

使用async/await可以使代码更具可读性和实用性:

async function getChatList() {
    const chats = await Chat.find({members: userId});
    const chatsList = [];
    for (const chat of chats) {
        let guestId = chat.members[1 - chat.members.indexOf(userId)];
        const guest = await User.findOne({_id: guestId});
        chatsList.push(guest.name);
    }
    return chatsList;
}

然后使用代码将聊天列表实际发送回用户:

try {
    return res.json(await getChatList());
} catch (err) {
    // handle errors;
}