异步索引数据库

Asynchronous indexeddb

我想用 indexeddb 在异步模式下做一些事情,但我失败了。你知道我的问题吗?

代码:

function test(basekey) {
    window.indexedDB.deleteDatabase("base");

    var lConnection = window.indexedDB.open("base", 1);
    lConnection.onsuccess = function (event) {
        var lDB = event.target.result;
        var lTransactionGlobal = lDB.transaction(['person'], 'readwrite');
        var lTransactionTable = lTransactionGlobal.objectStore('person');
        var lRequest = lTransactionTable.add({key: basekey+1, name:"me"});
        lRequest.onsuccess = function (e) {
            window.setTimeout(function () {
                console.log("Second wrote");
                lRequest = lTransactionTable.add({key: basekey+2, name:"you"});
                lRequest.onsuccess = function () {
                    lDB.close();
                };
                lRequest.onerror = function () {
                    console.error("Request 2 failed");
                };
            }, 0);
        };
        lRequest.onerror = function () {
            console.error("Request 1 failed");
        };
    };
    lConnection.onblocked = function () { console.log("Blocked"); };
    lConnection.onerror = function () { console.log("Error"); };
    lConnection.onupgradeneeded = function (pEvent) {
        var db = pEvent.target.result;
        var objectStore = db.createObjectStore("person", { keyPath: "key" });
    };
}

当我调用 test(0) 时,第二次出现此错误 "add":

Uncaught TransactionInactiveError: Failed to execute 'add' on 'IDBObjectStore': The transaction has finished.

我试过使用 Firefox 和 Chrome。

这是 Indexed DB 的一个设计要点:事务在没有进一步的请求需要处理时尝试立即提交。可以在创建后直接针对事务发出请求,也可以在处理程序中从该事务中先前请求的事件中发出请求。因此,您不能在 setTimeout() 回调中发出进一步的请求,因为 (1) 它不是来自请求的事件处理程序,并且 (2) 在您调用 setTimeout() 之后,没有进一步的工作,交易将开始尝试已经提交。

这是一个简化的示例,数字表示执行顺序。

function myFunc(connection) {
  var tx = connection.transaction('store');
  // (1) transaction is "active" here, you can make requests.
  var store = tx.objectStore('store');

  store.get('key1').onsuccess = function() {
    // (3) transaction is "active" again here.

    store.get('key2').onsuccess = function() {
      // (5) transaction is also "active" here.

      setTimeout(function() {
        // (7) transaction is "inactive" here, and the 
        // transaction will have attempted to commit,
        // so this will fail.
        store.get('key3'); // WILL THROW
      }, 0);

      // (6) transaction will be "inactive" when this returns.
      // Since there are no more pending requests it will attempt
      // to commit.
    };

    // (4) transaction will be "inactive" when this returns.
  };

  // (2) transaction will become "inactive" when control
  // returns to the event loop here.
}

这说明了两种情况:事务在 setTimeout() 回调中未处于活动状态,并且无论如何都在事务开始提交之后。但是您会分别看到与第一种情况相同的行为 - 例如如果你有一个长 运行 的交易(很多异步请求)并尝试在 setTimeout() 中做一些事情,即使有未完成的交易,例如在 (3) 之后移动 setTimeout()

您必须将您的工作分解为多个事务,或者首先完成所有非 IDB 异步工作。