Есть ли какое-либо решение для ожидания вложенного forEach, пока не вернется результат, используя async / await в node.js - PullRequest
3 голосов
/ 18 апреля 2019

Я работаю над FCM и мне нужны токены устройств для всех участников в канале / комнате, чтобы отправлять push-уведомления, и у каждого участника есть несколько устройств, для этого мне нужно два для цикла.

Я использую async / await с запросами Firestore, но он не ожидает результата, обрабатывает его в фоновом режиме и переходит к следующему оператору, которому нужны данные результата.

const notification = async (channelId) => {
    let tokens = []
    const members = await db.collection('channels/' + channelId + '/members').get();
    await members.forEach(async (member) => {
        const deviceTokens = await db.collection('users/' + member.id + '/devices').get();
        await deviceTokens.forEach(async (token) => {
            console.log(token.id);
            await tokens.push(token.data().token);
        })
    })
    console.log(tokens);
    return await sendPush(tokens); // calling other functions
}

Я ожидаю, что выходные данные будут tokens = ['token1', 'token2', 'token3'], но фактический вывод будет tokens = []

Ответы [ 2 ]

4 голосов
/ 18 апреля 2019

Полагаю, вы неправильно поняли использование async / await.

Вы можете использовать await для асинхронных функций.Array.forEach не является асинхронной функцией, поэтому вам не нужно использовать ее для внутреннего цикла.

Я бы порекомендовал использовать обещания здесь:

    async doStuff(){
        let tokens = [];
        const members = await db.collection('channels/' + channelId + '/members').get();

        let promises = members.map(member => {
            return db.collection('users/' + member.id + '/devices').get();
        })
        // or the forEach
        let promises = [];

        members.forEach(member => {
            promises.push(db.collection('users/' + member.id + '/devices').get());
        });

        Promise
            .all(promises)
            .then(values => {
                values.map(token => {
                    console.log(token.id);
                    tokens.push(token.data().token);
                });

                sendPush(tokens);
            })
    }

Теперь вы собрали все members, затем начали для всех members запросов токенов, и когда all() из них закончил васможете вставить ваши токены в этот массив и отправить их.

1 голос
/ 18 апреля 2019

forEach нельзя эффективно использовать вместе с async..await. Поскольку запрос возвращает снимок запроса , вместо этого следует выполнить итерацию массива. Цепочки обещаний могут выполняться последовательно с for..of или параллельно с Promise.all и массивом map, как объяснено в связанном вопросе , например ::

const notification = async (channelId) => {
    let tokens = [];
    const members = await db.collection('channels/' + channelId + '/members').get();
    for (const member of members.docs) {
      const deviceTokens = await db.collection('users/' + member.id + '/devices').get();
      for (const deviceToken of deviceTokens.docs) {
        tokens.push(deviceToken.data().token);
      }
    }

    return await sendPush(tokens);
}

await sendPush(...) будет работать правильно, только если sendPush вернет обещание.

...