我试图找出使用钩子在存储或更改对象时添加一些字段的最佳方法。
基本思想是有entry
个对象必须包含一堆基于某些复杂查询和其他条目计算的属性。这些计算出的属性都存储在名为derived
的属性下。每次需要时计算entry.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});
});
};
});
答案 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");
});
});
});