FirestoreでonSnapshotがadd/update/set/deleteの結果より先に呼ばれる


問題

FiresotreでCollectionの更新をListenしている場合、addの結果(id取得前)より先にonSnapshotが発火するようである。

自分自身がaddした結果かどうか判定する必要がある場合、addの結果のdoc IDが手に入っていない状態ではそれができない。

下記の例で、 Aのadd処理の後、Firebaseから処理が非同期的に帰ってきてBでDocumentのIDを取得できる。この時、自分が追加したデータのリストとしてmyDataに格納する。onSnapshotでは他人からのデータ追加も含めて通知がやってくるが、Bの前にCが発火するため、自分のデータを判定することができない。

var myData = [];

//......

firebase.firestore().collection("hoge").add({fuga: "fugu"}) // A
.then(async (docRef) => {
        myData.push(docRef.id);  // B
});

firebase.firestore().collection("hoge").onSnapshot((snapshot) => {
    snapshot.docChanges().forEach(async (change) => {
        if (change.type === "added") { // C
            if(myData.indexOf(change.doc.id)>=0){ /* 自分がaddしたデータ */ }
            else{ /* 自分以外のデータ, 処理 */ }
        }
    });
});

案0

自分自身が追加したデータも他からのデータも特に区別する必要のないような設計にする。これができれば一番良いし、原則これを目指すべきだろう。ただ諸事情によりできない場合もある。

案1

Record自体に一意性が十分保証されるような乱数等を入れおいて判定する。

var myData = [];

//......

var randv = rand(); // 本当はもっと堅牢なGUID生成
myData.push(randv);
firebase.firestore().collection("hoge").add({fuga: "fugu", token:randv}) // A
.then(async (docRef) => {
        // myData.push(docRef.id);  // B
});

firebase.firestore().collection("hoge").onSnapshot((snapshot) => {
    snapshot.docChanges().forEach(async (change) => {
        if (change.type === "added") { // C
            if(myData.indexOf(change.doc.data().token)>=0){ /* 自分がaddしたデータ */ }
            else{ /* 自分以外のデータ, 処理 */ }
        }
    });
});

案2

A -> Bの間にmyDataを参照する処理が入らなければ良い。同期の仕組みであるMonitor的なものを導入してそれを阻止する。こちらのMonitorクラスを使うと下記のようにかける。

var monitor = new Monitor();
var myData = [];

//.........

await monitor.enter();
firebase.firestore().collection("hoge").add({fuga: "fugu"}) // A
.then(async (docRef) => {
        myData.push(docRef.id);  // B
})
.finally(()=>{
        monitor.exit();
});

firebase.firestore().collection("hoge").onSnapshot((snapshot) => {
    snapshot.docChanges().forEach(async (change) => {
        if (change.type === "added") { // C
            await monitor.enter();
            if(myData.indexOf(change.doc.id)>=0){ /* 自分がaddしたデータ */ }
            else{ /* 自分以外のデータ, 処理 */ }
            monitor.exit();
        }
    });
});