Javascript IndexedDB:用承诺升级?

Javascript IndexedDB:用承诺升级?,javascript,promise,indexeddb,Javascript,Promise,Indexeddb,我刚刚用IndexedDb开始了我的第一个项目,我在尝试创建一个系统,以便在第一次使用时打开和升级数据库时遇到了麻烦。我想使用Promissions(当前服务,但我很灵活)为我提供一些关于捕获发生的任何错误的保证,并减少关于故障模式的思维开销。我的要求是: 使用者调用一些函数来打开和升级返回承诺的数据库 该功能按顺序执行所有必要的升级/迁移。如果没有发生错误,则通过连接到数据库来解决承诺 如果在任何阶段发生任何错误,承诺将被拒绝 添加新的迁移/升级步骤与定义执行升级的函数一样简单,所有其他并发

我刚刚用IndexedDb开始了我的第一个项目,我在尝试创建一个系统,以便在第一次使用时打开和升级数据库时遇到了麻烦。我想使用Promissions(当前服务,但我很灵活)为我提供一些关于捕获发生的任何错误的保证,并减少关于故障模式的思维开销。我的要求是:

  • 使用者调用一些函数来打开和升级返回承诺的数据库
  • 该功能按顺序执行所有必要的升级/迁移。如果没有发生错误,则通过连接到数据库来解决承诺
  • 如果在任何阶段发生任何错误,承诺将被拒绝
  • 添加新的迁移/升级步骤与定义执行升级的函数一样简单,所有其他并发问题都由“框架”处理
到目前为止我遇到的问题:

  • 如果数据库不需要升级,则不会调用
    onupgraderequired
    回调(因此,如果数据库不需要升级,则升级完成时得到解决的承诺永远不会得到解决,并且调用代码不知道连接回调时是否会出现这种情况)
  • 如果一个升级依赖于另一个升级(例如,填充刚创建的存储),则必须等待调用其
    onsuccess
    回调-因此每个升级都需要顺序链接
  • 看起来,在链中的前一个承诺解析之后执行承诺的延迟足以在再次需要之前将“事务”标记为非活动(我认为它们是用“nextTick”安排的,这可能与使事务非活动的机制相同)
  • update如果一个升级依赖于另一个升级,则在调用第一个升级的
    onsuccess
    回调时,
    versionchange
    事务不再处于活动状态
我目前的结论是,API从根本上反对基于承诺的方法。下面是我的最佳尝试(为了便于阅读,简化了一点)。我哪里做错了

var newPromise=函数(带延迟){
var deferred=$q.deferred();
试一试{
延期(延期);
}捕捉(错误){
延迟。拒绝(错误);
}
回报。承诺;
};
var newTransactionPromission=函数(getTransaction){
返回新承诺(功能(延迟){
var transaction=getTransaction();
transaction.oncomplete=函数(ev){deferred.resolve();};
transaction.onabort=函数(ev){deferred.reject(transaction.error);};
});
};
var迁移=[
功能(db){
返回newTransactionPromise(函数(){
//抛出:数据库未运行版本更改事务。
返回数据库
.createObjectStore(“条目”{keyPath:'id',autoIncrement:true})
交易
});
},
功能(db){
返回newTransactionPromise(函数()
{
var entryStore=db.transaction(“条目”,“读写”).objectStore(“条目”);
add({description:“First task”});
return entryStore.transaction;
});
}
];
var upgradeAndOpen=function(){
返回新承诺(功能(延迟){
var-latest_version=migrations.length;
var request=indexedDB.open(“铯”,最新版本);
request.onupgradeneeded=函数(事件){
试一试{
//创建一个已解决的承诺,以启动链
var setupDeferred=$q.deferred();
setupDeferred.resolve();
var setupComplete=setupDeferred.promise;
对于(var v=event.oldVersion;v
我终于找到了一种方法来避免此API的所有缺点,并找到了一种解决方案,该解决方案公开了一个干净的基于承诺的接口,并可推广到任意数量的数据库迁移。关键问题:

  • 架构更改只能在
    versionchange
    事务期间执行;但是数据更改不能在
    versionchange
    事务期间执行,因此我们必须区分数据迁移和模式迁移,并以不同的方式执行它们,使用不同的事务update数据更改可以在
    versionchange
    事务期间执行,但不能通过通常的
    db.transaction('readwrite',…).objectstore(…)
    方法执行-这会引发异常。而是使用对
    versionchange
    事务的引用
  • 为了允许模式创建和填充的任意交错,我们必须将它们视为单独的迁移步骤,并且在前一步骤的事务成功后仅尝试一个步骤
  • 规范明确禁止显式事务管理( )这限制了事务在事件循环完成后被标记为非活动的情况下可重复使用的范围
  • 因此,方法
    .open(dbName,version)
    只允许一个
    versionchange
    事务,一旦成功,它就消失了。此方法是创建
    versionchange
    事务的唯一方法
  • var newPromise = function(withDeferred) { var deferred = $q.defer(); try { withDeferred(deferred); } catch (err) { deferred.reject(err); } return deferred.promise; }; var newTransactionPromise = function(getTransaction) { return newPromise(function(deferred) { var transaction = getTransaction(); transaction.oncomplete = function(ev) { deferred.resolve(); }; transaction.onabort = function(ev) { deferred.reject(transaction.error); }; }); }; var newMigrationPromise = function(dbName, version, migration) { return newPromise(function(deferred) { var request = indexedDB.open(dbName, version); // NB: caller must ensure upgrade callback always called request.onupgradeneeded = function(event) { var db = request.result; newTransactionPromise( function() { migration(db, request.transaction); return request.transaction; }) .then(function() { db.close(); }) .then(deferred.resolve, deferred.reject); }; request.onerror = function(ev) { deferred.reject(request.error); }; }); }; var migrations = [ function(db, transaction) { db.createObjectStore("entries", { keyPath: 'id', autoIncrement: true }); }, function(db, transactionn) { db.createObjectStore("entries2", { keyPath: 'id', autoIncrement: true }); }, function(db, transaction) { var entryStore = transaction.objectStore("entries"); entryStore.add({description: "First task"}); } ]; var upgradeAndOpen = function() { return open() .then(function(db) { var version = db.version; db.close(); // this connection will block the upgrade (AFAICT) return version; }) .then(function(version) { return newPromise(function(deferred) { // version when created but empty is v1 // so after the first migration (index: 0) the version must be 2 var migrationsPerformed = version - 1; var migrationCount = migrations.length; var previousMigration = newPromise(function(deferred) { deferred.resolve(); }); for (var index = migrationsPerformed; index < migrationCount; index++) { var performNextMigration = newMigrationPromise.bind(this, "caesium", index+2, migrations[index]); previousMigration = previousMigration.then(performNextMigration); } previousMigration.then(deferred.resolve, deferred.reject); }); }) .then(open); }; var open = function() { return newPromise(function(deferred) { var request = indexedDB.open("caesium"); request.onsuccess = function(ev) { deferred.resolve(request.result); }; request.onerror = function(ev) { deferred.reject(request.error); }; }); }; upgradeAndOpen() .then(function() { $scope.status = "completed"; }, function(err) { $scope.status = err; });
      goog.db.openDatabase('mydb', 1, function(ev, db, tx) {
        db.createObjectStore('mystore');
      }).addCallback(function(db) {
        var putTx = db.createTransaction(
            [],
            goog.db.Transaction.TransactionMode.READ_WRITE);
        var store = putTx.objectStore('mystore');
        store.put('value', 'key');
        goog.listen(putTx, goog.db.Transaction.EventTypes.COMPLETE, function() {
          var getTx = db.createTransaction([]);
          var request = getTx.objectStore('mystore').get('key');
          request.addCallback(function(result) {
            ...
          });
      });
    
    var open = function(name, ver) {
      return new Promise(function(yes, no) {
         var req = indexedDB.open(name, var);
         req.onupgradedneeded = function(res) {
           no(req);
           req.onsuccess = null; // for clarity
         };
         req.onsuccess = function() {
           yes(res.result);
         };
         req.onblocked = no;
      }
    
    open('db name', 3).then(function(db) {
      // use db here
    
    }, function(req) {
      // version upgrade logic here
    
      if (req instanceof IDBResult) {
       return new Promise(function(yes, no) {
         req.transaction.createObjectStore('store_3');
         req.onsuccess = function() {
           yes(req.result);
         });
       });
     }