【问题标题】:firestore onSnapshot executing twicefirestore onSnapshot 执行两次
【发布时间】:2018-10-02 23:21:30
【问题描述】:

我的 firestore onSnapshot() 函数被调用了两次。

let user = firebase.firestore().collection('users').doc(userID).onSnapshot
({                    
    next: (documentSnapshot: firebase.firestore.DocumentSnapshot) =>
    {
         this.userArray.push(documentSnapshot as User);
         console.log(documentSnapshot);
         //here
    },
    error: (firestoreError: firebase.firestore.FirestoreError) =>
    {
         console.log(firestoreError);
         //here
    }
});

我也尝试过像https://firebase.google.com/docs/firestore/query-data/listen#detach_a_listener 一样通过在 //here 评论中包含 user() 来订阅,但无济于事。

如何修改使函数只执行一次,即每次只推送一个用户对象而不是两次。

【问题讨论】:

    标签: angular firebase google-cloud-firestore


    【解决方案1】:

    如果您需要一次性响应,请使用 .get() 方法进行承诺。

    firebase.firestore().collection('users').doc(userID).get().then(snap => {
      this.userArray = [...this.userArray, snap.doc);
    });
    

    但是,我建议使用AngularFire(完全有偏见,因为我维护了库)。它使处理常见的 Angular + Firebase 任务变得更加容易。

    【讨论】:

    • 这不是违背了使用onSnapshot的目的吗?据我了解,get() 不会监听变化。
    【解决方案2】:

    我不知道这是否与您的问题有关。如果有人使用

    firebase.firestore.FieldValue.serverTimestamp()
    

    给文档一个时间戳,然后 onSnaphot 会触发两次。这似乎是因为当您向数据库添加新文档时,onSnapshot 会触发,但 serverTimestamp 尚未运行。几毫秒后,serverTimestamp 将运行并更新您的文档 => onSnapshot 将再次触发。

    我想在 onSnapshot 触发之前添加一个小延迟(比如 0.5 秒左右),但我找不到这样做的方法。

    您也可以为 onCreate 事件创建一个服务器端函数,我相信这会解决您的问题。也许您的 userArray.push-action 更适合在服务器端执行。


    更新:要详细了解serverTimestamp() 的行为以及它触发两次侦听器的原因,请阅读这篇文章:The secrets of Firestore’s FieldValue.serverTimestamp() — REVEALED!。此外,官方文档指出:

    当您执行写入时,您的侦听器将收到新数据的通知在数据发送到后端之前

    在文章中有几个建议的解决方案,其中之一是使用snapshotmetadata 属性来查找metadata.hasPendingWrites 的布尔值是否为真(它告诉您快照您'正在查看尚未写入服务器)或false。

    例如,在您的情况下,您可以检查hasPendingWrites 是否为假,然后推送对象:

    if ( !documentSnapshot.metadata.hasPendingWrites ){
      // This code will only execute once the data has been written to the server 
      this.userArray.push(documentSnapshot as User);
      console.log(documentSnapshot);
    }
    

    在更通用的示例中,代码如下所示:

    firestore.collection("MyCollection")
    .onSnapshot( snapshot => {
    
        if ( snapshot.metadata.hasPendingWrites ){
            // Local changes have not yet been written to the backend
        } else {
            // Changes have been written to the backend
        }
    
    });
    

    在文档中找到的另一种有用的方法如下:

    如果你只想知道你的写入何时完成,你可以收听完成回调而不是使用hasPendingWrites。在 JavaScript 中,通过附加 .then() 回调来使用写入操作返回的 Promise。

    我希望这些资源和各种方法能够帮助任何试图找出解决方案的人。

    参考:

    【讨论】:

    • 就我而言,当从客户端设置FieldValue.serverTimestamp() 时,第一个通知包含相关字段的空值;第二个通知包含服务器时间戳值。我最终使用云功能来执行相同的逻辑,它为我解决了双重触发问题。
    • 在我的情况下,这是一个new Date() 用法,这很可能。内部触发firebase.firestore.FieldValue.serverTimestamp()调用
    • 这里也一样。
    • metadata.fromCache的值变化也可以触发更新
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-07
    • 2019-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多