indexedDB onupgradeneeded 事件永远不会完成

问题描述 投票:0回答:2

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

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

仅在现有数据库中创建和删除存储时存在问题。开头的 onupdateneeded (创建数据库并创建第一个存储)永远不会产生任何问题。

我尝试在升级之前关闭每个数据库连接,因为 2013 年的 Chromium 错误报告中提到了这个问题(标记为已修复:https://bugs.chromium.org/p/铬/问题/详细信息?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){
    }
}
javascript cross-browser indexeddb
2个回答
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.
};

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


0
投票

对于大量记录,在 Firefox 上删除对象存储(带有索引)可能会非常慢(数十分钟)。请参阅在 Firefox 上删除索引数据库存储可能会非常慢

所以它可能工作得很好,但花了这么长时间你(合理地)认为它失败了。

© www.soinside.com 2019 - 2024. All rights reserved.