Асинхронные изменения в хуке обновления Dexie

Я пытаюсь найти лучший подход к использованию хуков для добавления некоторых полей к объекту, когда он сохраняется или изменяется.

Основная идея состоит в том, что есть entry объекты, которые должны содержать набор свойств, основанных на некоторых сложных запросах и вычислениях других записей. Все эти вычисленные свойства хранятся в свойстве с именем derived. Было бы слишком дорого вычислять entry.derived каждый раз, когда это необходимо, или когда он считывается из БД. Вместо этого я решил заранее заполнить свойство 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});
            });
    };
});

person vopilif    schedule 22.11.2016    source источник


Ответы (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");
        });
    });
});
person David Fahlander    schedule 22.11.2016