javascript - Firebase Firestore - 异步/等待在继续之前不等待获取数据?
问题描述
我是 JS 的“异步/等待”方面的新手,我正在尝试了解它是如何工作的。
我得到的错误是以下代码的第 10 行。我已经创建了一个 firestore 数据库,并试图从 Collection 'rooms' 中监听并获取某个文档。我正在尝试从 doc 'joiner' 获取数据并使用该数据来更新其他元素的 innerHTML。
// References and Variables
const db = firebase.firestore();
const roomRef = await db.collection('rooms');
const remoteNameDOM = document.getElementById('remoteName');
const chatNameDOM = document.getElementById('title');
let remoteUser;
// Snapshot Listener
roomRef.onSnapshot(snapshot => {
snapshot.docChanges().forEach(async change => {
if (roomId != null){
if (role == "creator"){
const usersInfo = await roomRef.doc(roomId).collection('userInfo');
usersInfo.doc('joiner').get().then(async (doc) => {
remoteUser = await doc.data().joinerName;
remoteNameDOM.innerHTML = `${remoteUser} (Other)`;
chatNameDOM.innerHTML = `Chatting with ${remoteUser}`;
})
}
}
})
})
})
但是,我收到错误:
Uncaught (in promise) TypeError: Cannot read property 'joinerName' of undefined
同样,如果我将第 10-12 行更改为:
remoteUser = await doc.data();
remoteNameDOM.innerHTML = `${remoteUser.joinerName} (Other)`;
chatNameDOM.innerHTML = `Chatting with ${remoteUser.joinerName}`;
我犯了同样的错误。
我目前的理解是 await 将等待行/函数在继续之前完成,因此 remoteUser 在尝试调用它之前不应该为空。我会提到有时代码可以正常工作,并且 DOM 元素会更新并且没有控制台错误。
我的问题:我是否错误地考虑了 async/await 调用?这不是我应该如何从 Firestore 获取文件的方式吗?最重要的是,为什么它似乎只在某些时候起作用?
解决方案
您正在混合使用async/await
and then()
,不建议这样做。我在下面提出了一个解决方案,Promise.all()
该解决方案有助于理解代码中涉及的不同数组。您可以使用@Dharmaraj 建议async/await
的循环来调整它。for-of
roomRef.onSnapshot((snapshot) => {
// snapshot.docChanges() Returns an array of the documents changes since the last snapshot.
// you may check the type of the change. I guess you maybe don’t want to treat deletions
const promises = [];
snapshot.docChanges().forEach(docChange => {
// No need to use a roomId, you get the doc via docChange.doc
// see https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentChange
if (role == "creator") { // It is not clear from where you get the value of role...
const joinerRef = docChange.doc.collection('userInfo').doc('joiner');
promises.push(joinerRef.get());
}
});
Promise.all(promises)
.then(docSnapshotArray => {
// docSnapshotArray is an Array of all the docSnapshots
// corresponding to all the joiner docs corresponding to all
// the rooms that changed when the listener was triggered
docSnapshotArray.forEach(docSnapshot => {
remoteUser = docSnapshot.data().joinerName;
remoteNameDOM.innerHTML = `${remoteUser} (Other)`;
chatNameDOM.innerHTML = `Chatting with ${remoteUser}`;
})
});
});
但是,我不清楚的是您如何区分“第一” snapshot
(即roomRef.onSnapshot((snapshot) => {...}))
)的不同元素。如果有几个rooms
更改,则snapshot.docChanges()
Array 将包含多个更改,最后,您将覆盖最后一个循环中的remoteNameDOM
andchatNameDOM
元素。
或者您预先知道这个“第一个”snapshot
将始终包含一个文档(因为您的应用程序的架构),然后您可以通过只处理第一个和唯一的元素来简化代码,如下所示:
roomRef.onSnapshot((snapshot) => {
const roomDoc = snapshot.docChanges()[0];
// ...
});
推荐阅读
- haskell - 组成“总和的长度”是什么意思?
- angular - 如何验证 FormArray 中的 FormGroups
- sql - 如何将 SQL Server 存储过程的结果集从一台服务器导出到另一台服务器上的表中?
- html - 将数据 [缓冲区] 从 html 5 实时传输到 c# 套接字服务器的最佳方法是什么
- python - 无法在python中退出循环
- amazon-web-services - AWS Transit Gateway 的限制
- azure - azure 应用服务的构建日志在哪里?
- c++ - 关闭 QT GUI 后如何停止线程
- jenkins - Jenkins 声明性管道:未找到 npm 命令
- snowflake-cloud-data-platform - 雪花如何通过存储过程在阶段执行删除文件?