【问题标题】:Is there any solution to wait for nested forEach until return result using async/await in node.js是否有任何解决方案可以等待嵌套的 forEach 直到使用 node.js 中的 async/await 返回结果
【发布时间】:2019-09-08 20:24:51
【问题描述】:

我正在开发 FCM,需要频道/房间中所有成员的设备令牌来发送推送通知,并且每个成员都有多个设备,为此我需要两个 for 循环。

我正在使用 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 = []

【问题讨论】:

  • @estus 我询问了嵌套 forEach 请看一下
  • 我也被这个问题困扰,但没有找到任何解决方案。
  • @Umar 是否嵌套都没关系。您不应该将 forEach 与 async/await 一起使用,这就是重点,这在 dupe question 中进行了解释。
  • 我目前不使用 Firebase,无法检查它是否可行。我发布了一个答案。希望这会有所帮助。

标签: node.js callback async-await firebase-cloud-messaging google-cloud-functions


【解决方案1】:

forEach 不能有效地与async..await 一起使用。由于查询返回query snapshot,因此应该迭代一个数组。 Promise 链可以与for..of 串联执行,也可以与Promise.all 和数组map 并行执行,如related question 中所述,例如:

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 返回一个承诺时才能正常工作。

【讨论】:

  • 非常感谢@estus,你让我开心,犯了愚蠢的错误,再次感谢
【解决方案2】:

我猜你误解了 async/await 的用法。

您可以将 await 用于异步函数。 Array.forEach 不是异步函数,因此您不需要将它用于您的内部循环。

我建议在这里使用 Promise:

    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() 完成时,您可以将您的令牌推送到该数组中并发送它们。

【讨论】:

  • 使用 map 而不是 forEach 并忽略它返回的值是一种反模式。它已经返回了一个数组——如果members 是一个数组,它至少应该返回。此外,此代码不会导致正确链接的承诺。
  • 改变了 map 返回,idk 如果 op 想要从那个函数获取返回值
  • @BraveButter array.map 不适用于 firestore 查询结果
  • 你可以改成forEach应该没问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-14
  • 1970-01-01
  • 2019-04-08
  • 1970-01-01
  • 2018-02-21
相关资源
最近更新 更多