Как использовать обещание и перебирать коллекцию мангустов

Я создаю чат внутри своего сайта. Для хранения данных использую коллекции Chat, User, Messages.

Я хочу, чтобы результаты были в массиве, содержащем:

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

В модели чата у меня есть только chatid и array of two members, поэтому мне нужно прокрутить коллекцию пользователей, чтобы получить user name, используя user id из нее. Я хочу сохранить в массиве все имена (в будущем я также хотел бы перебирать сообщения, чтобы получать последние сообщения для каждого chatid). Проблема в том, что когда я возвращаю список чатов, он пуст. Я думаю, мне нужно как-то использовать 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);
    })

Куда ты возвращаешься chatsList и где пусто?

fjc 22.05.2019 16:32

В вашей модели чатов должно быть два пользователя-отправителя и получателя, и ваш первый запрос должен быть Chats.find({sender : {$in : 'массив, содержащий ваш идентификатор пользователя и еще один идентификатор пользователя'}, Receiver: {$in : 'массив, содержащий ваш идентификатор пользователя и еще один идентификатор пользователя'}})

JackOfAshes - Mohit Gawande 22.05.2019 16:34

@fjc в очереди return res.json(chatsList)

Angelzzz 22.05.2019 16:36

@JackOfAshes-MohitGawande Я храню его в одном массиве - members. members = [id1,id2], это для того, чтобы получить гостя чата, который я использую guestId

Angelzzz 22.05.2019 16:37

@Angelzzz: так в каком порядке вы будете следовать в очереди let guestId = chat.members[1 - chat.members.indexOf(userId)]; вы принимаете какой-то порядок. Верно? как вы убедитесь, что кто-то является гостем, если вы используете приложение, которое будет гостем, и если кто-то еще использует приложение, вы будете гостем.

Vishwa Deepak Singh 22.05.2019 16:44

@VishwaDeepakSingh есть массив с двумя пользователями (индекс 0 и 1). Если я пользователь 0, то гость - это пользователь 1 и наоборот :), поэтому guestId всегда является идентификатором гостя, а не моим

Angelzzz 22.05.2019 16:46

@Angelzzz: guestId — это совершенно другой атрибут? Спрашиваю, потому что я вижу в коде, что вы управляете этим chat.members[1 - chat.members.indexOf(userId)] так.

JackOfAshes - Mohit Gawande 22.05.2019 16:47

Пожалуйста, забудьте о guestid. Я уверен, что в этой строке это работает нормально. Не понимаю, почему. Вы спрашиваете об этом. В этом случае он ничего не делает. В этом примере это может быть жестко запрограммировано на любое число.

Angelzzz 22.05.2019 16:57

@Angelzzz: не могли бы вы проверить, работает ли решение, представленное в ответах?

JackOfAshes - Mohit Gawande 22.05.2019 17:07
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Сравнение структур данных: Массивы и объекты в Javascript
Сравнение структур данных: Массивы и объекты в Javascript
Итак, вы изучили основы JavaScript и хотите перейти к изучению структур данных. Мотивация для изучения/понимания Структур данных может быть разной,...
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
Приложение для отслеживания бюджета на React js для начинающих
Приложение для отслеживания бюджета на React js для начинающих
Обучение на практике - это проверенная тема для достижения успеха в любой области. Если вы знаете контекст фразы "Практика делает человека...
Стоит ли использовать React в 2022 году?
Стоит ли использовать React в 2022 году?
В 2022 году мы все слышим о трендах фронтенда (React, Vue), но мы не знаем, почему мы должны использовать эти фреймворки, когда их использовать, а...
1
9
53
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Действительно, Обещание.все — это то, что вы ищете:

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);
        });
    });
});

хотя, вероятно, было бы лучше сделать один вызов БД со списком идентификаторов ($in запрос). Что-то вроде этого:

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.

Спасибо! Можете, пожалуйста, подробнее рассказать об одиночном вызове БД? Как это сделать?

Angelzzz 22.05.2019 16:38

что, если я хочу сделать chatsList массивом объектов и добавить в него chatId?

Angelzzz 23.05.2019 19:45

Вы можете попробовать это:

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);
})

как это может помочь? Меня устраивает, как я получаю guestId, все работает хорошо

Angelzzz 22.05.2019 16:40

Это вообще не отвечает на вопрос.

freakish 22.05.2019 16:43

Это не дает ответа на вопрос. Чтобы подвергнуть критике или запросить разъяснения у автора, оставьте комментарий под его публикацией. - Из обзора

Towkir 22.05.2019 16:55

@Towkir - не могли бы вы просмотреть еще раз. Извините, я не смог понять вопрос с первой попытки.

JackOfAshes - Mohit Gawande 22.05.2019 17:00

@Angelzzz: Мой Плохой.

JackOfAshes - Mohit Gawande 22.05.2019 17:01

@Angelzzz: в таком случае не могли бы вы проголосовать за мой ответ. Я потерял свою репутацию из-за моего безответственного поведения здесь.

JackOfAshes - Mohit Gawande 22.05.2019 22:34

Вы сталкиваетесь с проблемами параллелизма. Например, запуск chats.forEach, а внутри forEach запуск User.findOne().then: оператор return уже выполнен до того, как обещание 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;
}

Другие вопросы по теме