开发者问题收集

为什么异步 firebase 获取不起作用?(NODE JS)

2021-06-08
292

构建 NodeJS REST API。 尝试从 FireBase 集合发送加载数据,然后将其发送给用户(作为 API 响应)。 问题似乎在于它没有等待 FireBase 获取解析,而是返回没有集合数据的响应。(尝试使用 ASYNC-AWAIT,但不起作用)

exports.getChatMessages = async (req, res, next) => {
  const chatId = req.params.chatId
  const getChatData = () => {
    db
      .collection('chats')
      .doc(chatId)
      .collection('messages')
      .orderBy('timeStamp', 'asc')
      .onSnapshot((snapshot) => {
        snapshot.docs.forEach(msg => {
          console.log(msg.data().messageContent)
          return {
            authorID: msg.data().authorID,
            messageContent: msg.data().messageContent,
            timeStamp: msg.data().timeStamp,
          }
        })
      })
  }
  try {
    const chatData = await getChatData()
    console.log(chatData)
    res.status(200).json({
      message: 'Chat Has Found',
      chatData: chatData
    })
  } catch (err) {
    if (!err.statusCode) {
      err.statusCode(500)
    }
    next(err)
  }
}

如您所见,我使用了 2 个 console.log 来实现问题所在,终端日志如下所示:

  • [](来自 console.logs(chatData))
  • 所有消息(来自 console.log(msg.data().messageContent))

有没有办法阻止代码,直到真正获取 FireBase 数据?

3个回答

如果我理解正确的话,您想要发回 messages 子集合中存在的所有文档的数组。以下应该可以解决问题。

  exports.getChatMessages = async (req, res, next) => {
    const chatId = req.params.chatId;
    const collectionRef = db
      .collection('chats')
      .doc(chatId)
      .collection('messages')
      .orderBy('timeStamp', 'asc');

    try {
      const chatsQuerySnapshot = await collectionRef.get();
      const chatData = [];
      chatsQuerySnapshot.forEach((msg) => {
        console.log(msg.data().messageContent);
        chatData.push({
          authorID: msg.data().authorID,
          messageContent: msg.data().messageContent,
          timeStamp: msg.data().timeStamp,
        });
      });
      console.log(chatData);
      res.status(200).json({
        message: 'Chat Has Found',
        chatData: chatData,
      });
    } catch (err) {
      if (!err.statusCode) {
        err.statusCode(500);
      }
      next(err);
    }
  };

异步 ​​ get() 方法返回一个 QuerySnapshot ,您可以在其上调用 forEach() 来枚举 QuerySnapshot 中的所有文档。

Renaud Tarnec
2021-06-08

您只能 await 一个 Promise。目前, getChatData() 不返回 Promise,因此等待它是没有意义的。您正在尝试等待一个固定值,因此它会立即解析并跳转到下一行。 console.log(chatData) 发生。然后,稍后,您的 (snapshot) => 回调会发生,但为时已晚。

const getChatData = () => new Promise(resolve => { // Return a Promise, so it can be awaited
    db.collection('chats')
        .doc(chatId)
        .collection('messages')
        .orderBy('timeStamp', 'asc')
        .onSnapshot(resolve) // Equivalent to .onSnapshot((snapshot) => resolve(snapshot))
})


const snapshot = await getChatData();
console.log(snapshot)

// Put your transform logic out of the function that calls the DB. A function should only do one thing if possible : call or transform, not both.
const chatData = snapshot.map(msg => ({
    authorID: msg.data().authorID,
    messageContent: msg.data().messageContent,
    timeStamp: msg.data().timeStamp,
}));

res.status(200).json({
    message: 'Chat Has Found',
    chatData
})
Jeremy Thille
2021-06-08

现在, getChatData 是这样的(简短版本):

const getChatData = () => {
  db
    .collection('chats')
    .doc(chatId)
    .collection('messages')
    .orderBy('timeStamp', 'asc')
    .onSnapshot((snapshot) => {}) // some things inside
}

这意味着 getChatData 函数 调用 一些数据库查询,然后返回 void (无)。我敢打赌你会想要 返回数据库调用 (希望它是一个 Promise),这样你的 await 就能为你做一些工作。类似这样的代码:

const getChatData = async () => 
  db
    .collection('chats')
    // ...

这与 const getChatData = async() => 相同{ return db... }


更新 :现在我再次查看了文档,我发现您使用了 onSnapshot ,它用于 更新 并且可以多次触发。第一次调用实际上发出了一个请求,但随后继续监听这些更新。由于这看起来像是一个常规的请求-响应,并且您希望它只发生一次 - 使用 .get() docs 而不是 .onSnapshot() 。否则那些监听器会留在那里并造成麻烦。 .get() 返回一个 Promise,因此我上面提到的示例修复将完美运行,您无需更改代码的其他部分。

Andrey Popov
2021-06-08