2

オブジェクトが保存または変更されたときにフックを使用してオブジェクトにいくつかのフィールドを追加するための最良の方法を見つけようとしています。

基本的な考え方はentry、いくつかの複雑なクエリと他のエントリの計算に基づく一連のプロパティを含む必要があるオブジェクトがあるということです。これらの計算されたプロパティはすべて、 というプロパティの下に保存されますderivedentry.derived必要なたびに、または DB から読み取られるたびに計算すると、非常にコストがかかります。代わりに、事前にプロパティを設定することを選択しましたが、derivedフックはこれを行うのに最適な場所のようです。

これはcreatingフックには問題ないようです。derivedただし、他のプロパティentryが変更された場合は、再生成する必要もあります。このupdatingフックでは、変更を返すことによって追加の変更を送信する必要があります。これは、変更を生成できる唯一の方法が非同期呼び出しによるものであるため、問題があります。

以下は、問題を実証しようとする最小限のコードです。オプションはBまだ試していませんが、うまくいかないと思います。

const entryDerivedData = function(entry) {
    // query a bunch of data from entries table then do some calculations
    return db.entries.where('exerciseID').equals(entry.exerciseID).toArray()
        .then(e => {
            // do some calculation and return
            return calculateDerivedData(e);
        });
};

// A: Can't do this because `hook` isn't expecting a generator func
db.entries.hook('updating', function*(mods, primKey, entry, transaction) {
    const derived = yield entryDerivedData(entry);
    return derived;
});

// B: Another possibility, but probably won't work either
db.entries.hook('updating', function(mods, primKey, entry, transaction) {
    transaction.scopeFunc = function() {
        return entryDerivedData(entry)
            .then(derived => {
                // Won't this result in another invocation of the updating hook?
                return db.entries.update(entry.id, {derived});
            });
    };
});
4

1 に答える 1

1

残念ながら、フックは同期的であり、フック内で非同期呼び出しを行う方法はありません。これは変更される予定ですが、いつになるかは約束できません。今後 6 か月以内にフック フレームワークを書き直し、非同期にできるバルク フック (よりパフォーマンスの高い) を許可する (既存のフックの下位互換性を維持する) ことを期待しています。

それまでは、(ユーザーが明示的なトランザクションを実行するかどうかに関係なく) フックが常にトランザクション内で呼び出されるという事実を利用でき、現在のトランザクションに対して追加の操作を実行できます。追加の変更によってフックが再びトリガーされる可能性があるため、無限ループに陥らないようにする必要があります。

例は次のとおりです。

db.entries.hook('creating', (primKey, entry, trans) => {
  entryDerivedData(entry).then(derived => {
        db.entries.update(primKey, { derived }).catch (e => {
           // Failed to update. Abort transaction by rethrow error:
           throw new Error ("Could not make sure derived property was set properly");
        });
    });
});

db.entries.hook('updating', (mods, primKey, entry, trans) => {
    if ('derived' in mods) return; // We're the one triggering this change. Ignore.
    // First, apply the mods onto entry:
    var entryClone = Dexie.deepClone(entry);
    Object.keys(mods).forEach(keyPath => {
        if (mods[keyPath] === undefined)
            Dexie.delByKeyPath(entryClone, keyPath);
        else
            Dexie.setByKeyPath(entryClone, keyPath, mods[keyPath]);
    });

    entryDerivedData(entryClone).then(derived => {
        db.entries.update(primKey, { derived }).catch (e => {
           // Failed to update. Abort transaction by rethrow error:
           throw new Error ("Could not make sure derived property was set properly");
        });
    });
});
于 2016-11-22T10:27:14.633 に答える