英文:
Firebase Firestore Listener Fires Multiple Times
问题
我正在使用Firebase Firestore用于iOS应用程序。
我有一个承诺函数,用于循环遍历数组,并通过Firestore批量查询为每个数组项编写文档。
同时,我在该集合上有一个监听器,我的函数在其中写入文档。
为了避免监听器反复触发,我在运行承诺函数之前移除监听器。一旦承诺函数完成,我会在承诺函数的完成处理程序中重新附加监听器。
我的问题是,尽管我已经这样做了,但我注意到监听器在重新附加后会反复触发,而不只触发一次。
任何指导都将不胜感激。我尝试通过 DispatchQueue.asyncAfter
延迟重新附加监听器,观察到监听器触发的次数减少,所以问题可能与网络调用的延迟在Firestore中反映/执行有关吗?
更新:我将批处理操作拆分为单独的操作,包括写操作和删除操作。在其中一个写操作的完成处理程序中,我完成了承诺函数。这似乎提供了对我的问题的一种解决方法...但让我担心其他地方的批处理操作,是否有更好的解决方案... :/
private var listener: ListenerRegistration?
func writeTo(ids: [String]) -> Promise<Void> {
toggleListener(isActive: false)
return Promise.value(ids).thenMap { id in
Promise<String> { resolver in
let batch = self.db.batch()
let refOne = self.db.collection(collectionOne).document(id)
let refTwo = self.db.collection(collectionOne).document(id).collection(collectionTwo).document()
batch.setData(["x": "x"], forDocument: refOne)
batch.setData(["x": "x"], forDocument: refTwo)
batch.commit { error in
resolver.fulfill(id)
}
}
}
.done { uids in
toggleListener(isActive: true)
}
}
func toggleListener(isActive: Bool) {
if isActive {
listener = db.collection(collectionOne).addSnapshotListener { snapshot, error in
print("listening")
}
} else {
listener?.remove()
listener = nil
}
}
英文:
I am using Firebase Firestore for an iOS app.
I have a promise function I run that loops through an array and writes a document for each array item via a Firestore batch query.
In parallel, I have a listener on the collection which my function writes documents under.
To avoid having the listener fire repeatedly, I remove the listener prior to running the promise function. Once the promise function is complete, I reattach the listener in the promise function's completion handler.
My problem is, despite having done this, I notice that the listener fires repeatedly after attaching instead of just firing once.
Any guidance would be greatly appreciated. I tried delaying reattaching the listener via DispatchQueue.asyncAfter
and have observed the number of times the listener fire go down, so perhaps the issue relates to a delay in the network call being reflected/executed in Firestore?
> Update: I split the batch operation into individual operations, which
> included both write and delete operations. In one of the write
> operations' completion handlers, I fulfill the promise function. This
> seems to offer a workaround for my issue...but does have me worried
> about other batch operations I have elsewhere and wonder if there is
> altogether better solution... :/
private var listener: ListenerRegistration?
func writeTo(ids: [String]) -> Promise<Void> {
toggleListener(isActive: false)
return Promise.value(ids).thenMap { id in
Promise<String> { resolver in
let batch = self.db.batch()
let refOne = self.db.collection(collectionOne).document(id)
let refTwo = self.db.collection(collectionOne).document(id).collection(collectionTwo).document()
batch.setData(["x": "x"], forDocument: refOne)
batch.setData([ "x": "x"], forDocument: refTwo)
batch.commit { error in
resolver.fulfill(id)
}
}
}
.done { uids in
toggleListener(isActive: true)
}
}
func toggleListener(isActive: Bool) {
if isActive {
listener = db.collection(collectionOne).addSnapshotListener{ snapshot, error in
print("listening")
}
} else {
listener?.remove()
listener = nil
}
}
答案1
得分: 1
以下是翻译好的部分:
问题在于批量写操作被发送到Firestore作为一个单一请求,但Firestore会独立处理批次中的每个写入操作。这意味着如果一个写入成功而另一个失败,监听器可能会被触发多次 - 对每个成功的写入都会触发一次。
因此,更好的方法是使用事务来确保所有写入操作一起以原子方式处理,这样监听器只会被触发一次。这就是为什么使用事务而不是批量写入可以帮助您避免多次触发监听器。
这里我使用了事务而不是批量写入:
func writeTo(ids: [String]) -> Promise<Void> {
toggleListener(isActive: false)
return Promise.value(ids).thenMap { id in
self.db.runTransaction { transaction -> String in
let refOne = self.db.collection(collectionOne).document(id)
let refTwo = refOne.collection(collectionTwo).document()
transaction.setData(["x": "x"], forDocument: refOne)
transaction.setData(["x": "x"], forDocument: refTwo)
return id
}
}.done { uids in
toggleListener(isActive: true)
}
}
func toggleListener(isActive: Bool) {
if isActive {
if listener == nil {
listener = db.collection(collectionOne).addSnapshotListener { snapshot, error in
print("listening")
}
}
} else {
listener?.remove()
listener = nil
}
}
在这里,我们使用runTransaction
而不是批量写入。事务确保对refOne
和refTwo
的写入是以原子方式应用的,因此监听器只会被触发一次。
英文:
The issue is that the batched writes are sent as a single request to Firestore, but Firestore processes each write in the batch independently. This means that if one write succeeds and another fails, the listener could be triggered multiple times - once for each successful write.
So a better approach would be to use a transaction that ensures that all writes are processed together, atomically, so the listener will only be triggered once. This is why using a transaction instead of a batch write can help you avoid multiple listener triggers.
Here I have use Transactions instead of batch writes:
func writeTo(ids: [String]) -> Promise<Void> {
toggleListener(isActive: false)
return Promise.value(ids).thenMap { id in
self.db.runTransaction { transaction -> String in
let refOne = self.db.collection(collectionOne).document(id)
let refTwo = refOne.collection(collectionTwo).document()
transaction.setData(["x": "x"], forDocument: refOne)
transaction.setData(["x": "x"], forDocument: refTwo)
return id
}
}.done { uids in
toggleListener(isActive: true)
}
}
func toggleListener(isActive: Bool) {
if isActive {
if listener == nil {
listener = db.collection(collectionOne).addSnapshotListener { snapshot, error in
print("listening")
}
}
} else {
listener?.remove()
listener = nil
}
}
Here, we're using runTransaction
instead of a batch write. The transaction ensures that the writes to refOne and refTwo are applied atomically, so the listener will only be triggered once.
Reference : Batched writes and Updating data with transactions
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论