2016-11-22 2 views
1

Я пытаюсь найти лучший подход для использования крючков для добавления некоторых полей в объект, когда он будет сохранен или изменен.Изменения Async в обновляющем крючке 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}); 
      }); 
    }; 
}); 

ответ

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"); 
     }); 
    }); 
}); 
Смежные вопросы