indexedDB onupgradeneeded事件永远不会完成

时间:2017-04-04 18:43:42

标签: javascript cross-browser indexeddb

在我的应用中,用户应该能够动态创建和删除objectStores 。感谢indexedDB,这个操作(为什么在世界上)只允许在 onupgradeneeded 中。

触发 onupgradedneeded 后,indexedDB永远不会完成此请求,并且gc永远不会收集此连接。

在现有数据库中创建和删除存储时只存在问题。开头的 onupdateneeded (创建db并创建第一个商店)从未出现任何问题。

我尝试在升级之前关闭每个数据库连接,因为2013年有一个针对Chromium的错误报告,其中提到了这个问题(标记为已修复:https://bugs.chromium.org/p/chromium/issues/detail?id=242115)。

事实上,我没有找到关于这个问题的任何实际信息,这让我想到我在我的代码中做错了...... 但是为什么有些浏览器只是做他们的工作?

这是我重现此错误的代码:

db.js

// Init IndexedDB
var dbversion;
var oStore;
var dbname = "UserName1"; // == Username
initDB();


function initDB() {
    window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
    window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange

    if (!indexedDB) {
       window.alert("Datenbanksupport im Browser fehlt !")
    }else{
        console.log("indexedDB: Supported !");
        // >> first visit ?
        var vCheck = indexedDB.open(dbname);
        vCheck.onerror = errorHandler;
        vCheck.onversionchange = function(ev) {
            vCheck.close();
        };
        vCheck.onsuccess = function(event){
            var db = vCheck.result;
            dbversion = parseInt(localStorage.getItem("dbversion"));
            if (!dbversion){
                // -- maybe
                dbversion = parseInt(db.version);
                if (dbversion <= 1){
                    // -- definitely
                    var needUpgrade = false;
                    db.close();
                    var request = indexedDB.open(dbname, dbversion+1);
                    request.onerror = errorHandler;
                    request.onupgradeneeded = function(event){
                        console.log("indexedDB: start upgrade");
                        var nextDB = request.result;
                        if (!nextDB.objectStoreNames.contains('account')) {
                            nextDB.createObjectStore('account', {keyPath: "id", autoIncrement: true});
                        }
                        dbversion += 1;
                        localStorage.setItem("dbversion", dbversion);
                        needUpgrade = true;
                        console.log("indexedDB: upgrade finished");
                    }
                    request.onsuccess = function(event){
                        if (needUpgrade) {
                            var objectStore = event.target.result.transaction(['account'], "readwrite").objectStore("account");
                            row = {
                                'id' : 0,
                                'username' : dbname,
                            };
                            objectStore.add(row);
                        }
                        console.log("indexedDB: init");
                    };
                    request.oncomplete = function(event){request.result.close();}
                }else{
                    // -- no
                    localStorage.setItem("dbversion", dbversion);
                    vCheck.oncomplete = console.log("indexedDB: dbversion unknown (not in localStorage)");
                }
            }else{
                // -- no
                console.log("indexedDB: version", dbversion);
                console.log("indexedDB: init");
            }
        }
    }
}

function listStores() {
    var request = indexedDB.open(dbname, dbversion);
    request.onerror = errorHandler;
    request.onsuccess = function(event){
        var db = request.result;
        var allStores = db.objectStoreNames;
        console.log(allStores);
    }
    request.oncomplete = function(event){request.result.close();}
}

function addStore(storeName) {
    dbversion = localStorage.getItem("dbversion");
    var request = indexedDB.open(dbname, dbversion);
    request.onerror = errorHandler;
    request.onsuccess = function(event){
        var db = request.result;
        if (!db.objectStoreNames.contains(storeName)) {
            dbversion = parseInt(dbversion) + 1
            localStorage.setItem("dbversion", dbversion);
            db.close()
            var nextRequest = indexedDB.open(dbname, dbversion);
            nextRequest.onversionchange = function(ev) {
                nextRequest.close();
            };
            nextRequest.onupgradeneeded = function(event) {
                console.log("indexedDB: creating");
                var nextDB = nextRequest.result;
                nextDB.createObjectStore(storeName, {keyPath: "id", autoIncrement: true});
                oStore = storeName;
                console.log("indexedDB:", storeName, "created");
            }
            nextRequest.onerror = errorHandler;
            nextRequest.oncomplete = function(event){nextRequest.result.close();}
        }else{
            oStore = storeName;
            console.log("indexedDB:", storeName, "already exists. Do nothing...");
        }
    }
}

function selectStore(storeName){
    oStore = storeName;
    console.log("indexedDB: globalVar oStore ==", oStore);
}

function addMember(data) {
    var request = indexedDB.open(dbname, dbversion);
    request.onerror = errorHandler;
    request.onsuccess = function(event){
        var db = request.result;
        var objectStore = db.transaction([oStore], "readwrite").objectStore(oStore);
        row = {
            'id' : Date.now(),
            'name' : {
                'vname' : data['vname'],
                'nname' : data['nname'],
            },
        }
        objectStore.add(row);
        console.log("indexedDB:", row["id"], "inserted");
    }
    request.oncomplete = function(event){request.result.close();}
    return;
}

function deleteStore(storeName) {
    dbversion = localStorage.getItem("dbversion");
    dbversion = parseInt(dbversion) + 1
    localStorage.setItem("dbversion", dbversion);
    var request = indexedDB.open(dbname, dbversion);
    request.onerror = errorHandler;
    request.onupgradeneeded = function(event){
        var db = request.result;
        db.deleteObjectStore(storeName);
        console.log("indexedDB:", storeName, "deleted");
    }
    request.oncomplete = function(event){request.result.close();}
}

function errorHandler(event) {
    console.log("indexedDB: operation went wrong:");
    console.log("indexedDB:", event);
    return;
}

调用此函数顺序

listStores()
addStore('TestStore')
selectStore('TestStore')
addMember({'vname':'John', 'nname':'Doe'})
deleteStore('TestStore')
listStores() // <<<<<<<<<<<< hangs up !

编辑:

当约书亚结束时,关闭连接的地方是错误的。此外还有一个非常重要的事实:

在您快速删除objectStore之前,您必须先清除它!

这是正确的deleteStore()

function deleteStore(storeName) {
    // First: Clear the store
    var request1 = indexedDB.open(dbname, dbversion);
    request1.onsuccess = function(event){
        var connection = event.target.result;

        var objectStore = connection.transaction([storeName], "readwrite").objectStore(storeName);
        var result_clear = objectStore.clear();
        result_clear.onerror = errorHandler;
        result_clear.onsuccess = function(event){
            console.log("indexedDB: clearing Store...");
            // Second: Delete the store
            dbversion += 1;
            localStorage.setItem("dbversion", dbversion);
            var request2 = indexedDB.open(dbname, dbversion);
            request2.onsuccess = function(event){
                var connection2 = event.target.result;

                // ---> Garbage Collection
                connection2.onversionchange = function(event) {
                    connection2.close();
                };

            }
            request2.onerror = errorHandler;
            request2.onupgradeneeded = function(event){
                var connection2 = event.target.result;
                connection2.deleteObjectStore(storeName);
                console.log("indexedDB:", storeName, "deleted");
            }
        }

        // ---> Garbage Collection
        connection.onversionchange = function(event) {
            connection.close();
        };

    }
    request1.onerror = errorHandler;
    request1.oncomplete = function(e){
    }
}

1 个答案:

答案 0 :(得分:1)

确保您正在查看针对该连接触发的versionchange事件,而不是已打开的请求。

var openRequest = indexedDB.open(name, version);
openRequest.onblocked = function(e) {
  // Another connection is open, preventing the upgrade,
  // and it didn't close immediately.
};
openRequest.onerror = function(e) {
  // Open failed - possibly the version is higher than requested.
};
openRequest.onupgradeneeded = function(e) {
  // Check the current version, and upgrade as needed.
  var connection = openRequest.result;
  if (e.oldVersion < 1) {
    // db is new - create v1 schema
  }
  if (e.oldVersion < 2) {
    // upgrade v1 to v2 schema
  }
  if (e.oldVersion < 3) {
    // upgrade v2 to v3, etc
  }
};
openRequest.onsuccess = function(e) {
  var connection = openRequest.result;
  connection.onversionchange = function(e) {
    // Close immediately to allow the upgrade requested by another
    // instance to proceed.
    connection.close();
  };

  // The connection is open - use it for stuff.
};

上面还演示了版本控制的常用模式 - 您的代码在打开时请求特定版本,并在需要时升级旧架构。当您的应用程序需要新商店时,您可以增加版本号并在升级所需的处理程序中引入一个额外的步骤。