Firebase Firestore - Async/Await 在继续之前不等待获取数据吗?
我对 JS 的“async/await”方面还不熟悉,我正在尝试了解它的工作原理。
我收到的错误是以下代码的第 10 行。我创建了一个 firestore 数据库,并尝试从 Collection 'rooms' 中监听并获取某个文档。我正在尝试从文档“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}`;
})
}
}
})
})
})
但是,我收到了错误:
未捕获(在承诺中)TypeError:无法读取未定义的属性“joinerName”
同样,如果我将第 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
和
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}`;
})
});
});
但是,我不清楚的是您如何区分“第一个”
快照
的不同元素(即
roomRef.onSnapshot((snapshot) => {...}))
)。如果多个
rooms
发生变化,则
snapshot.docChanges()
数组将包含多个更改,最后,您将在最后一个循环中
覆盖
remoteNameDOM
和
chatNameDOM
元素。
或者您预先知道这个“第一个”
快照
将始终包含单个文档(由于应用程序的架构),然后您可以通过仅处理第一个和唯一元素来简化代码,如下所示:
roomRef.onSnapshot((snapshot) => {
const roomDoc = snapshot.docChanges()[0];
// ...
});
这里面有几个错误:
- db.collection() 不返回承诺,因此 await 在那里不是必需的
- forEach 忽略承诺,因此您实际上不能在 forEach 中使用 await。在这种情况下,for-of 是首选。
请尝试以下代码:
const db = firebase.firestore();
const roomRef = db.collection('rooms');
const remoteNameDOM = document.getElementById('remoteName');
const chatNameDOM = document.getElementById('title');
let remoteUser;
// Snapshot Listener
roomRef.onSnapshot(async (snapshot) => {
for (const change of snapshot.docChanges()) {
if (roomId != null){
if (role == "creator"){
const usersInfo = roomRef.doc(roomId).collection('userInfo').doc("joiner");
usersInfo.doc('joiner').get().then(async (doc) => {
remoteUser = doc.data().joinerName;
remoteNameDOM.innerHTML = `${remoteUser} (Other)`;
chatNameDOM.innerHTML = `Chatting with ${remoteUser}`;
})
}
}
}
})