2

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

基本思想是有些entry对象必须包含一堆基于一些复杂查询和其他条目计算的属性。这些计算出来的属性都存储在一个名为 的属性下derivedentry.derived每次需要它或从数据库中读取它时,计算它的成本会非常高。相反,我选择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});
            });
    };
});
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 回答