Dexie更新钩子中的异步更改

时间:2016-11-22 05:43:29

标签: dexie

我试图找出使用钩子在存储或更改对象时添加一些字段的最佳方法。

基本思想是有entry个对象必须包含一堆基于某些复杂查询和其他条目计算的属性。这些计算出的属性都存储在名为derived的属性下。每次需要时计算entry.derived或从DB读取它都会非常昂贵。相反,我选择提前填充derived属性,钩子似乎是最好的地方。

creating钩子似乎没问题。但是,如果derived中的任何其他属性发生更改,我还需要重新生成entryupdating挂钩要求我通过返回它们来提交其他更改,这是有问题的,因为我可以通过异步调用生成更改的唯一方法。

下面是一些试图证明问题的最小代码。我还没有尝试过选项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});
            });
    };
});

1 个答案:

答案 0 :(得分: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");
        });
    });
});