【问题标题】:IndexedDB w/ two transactions: 1 read then 1 update带有两个事务的 IndexedDB:1 个读取然后 1 个更新
【发布时间】:2019-06-12 01:43:50
【问题描述】:

使用 IndexedDB,一旦我已经创建了读取和显示数据的事务,我似乎无法为我的对象存储创建第二个事务(只是 ToDo 的列表)。我在尝试创建第二个事务的行上没有收到错误,但没有任何反应,代码停止执行。

检索和显示我的数据的这部分运行良好:

var trans;
var ostore;
var db;
var reqdb = window.indexedDB.open("ToDoApp", 2);


reqdb.onupgradeneeded = function (event) {
    console.log("running onupgradeneeded");
    var myDB = event.target.result;
    if (!myDB.objectStoreNames.contains("todos")) {
        myDB.createObjectStore("todos", { keyPath: "ToDoTitle" });
    }
};


reqdb.onsuccess = function (event) {
    db = event.target.result;
    trans = db.transaction(["todos"], "readonly");
    ostore = trans.objectStore("todos");
    var req = ostore.get("TEST IDB 2");   
    $("#btnSave").click(function () {
        UpdateToDo();
    });
    req.onerror = function (event) {
        alert("error");
    };
    req.onsuccess = function (event) {
        $("#ToDoTitle").val(req.result.ToDoTitle);
    };
};

这可以很好地获取和显示内容。但请注意使用 onclick 事件设置的 UpdateToDo() 函数,因此我可以实际更新我的数据。

function UpdateToDo(event) {
    alert("1"); 
    var newtransaction = db.transaction(["todos"], "readwrite");
    alert("2");
    var newstore = newtransaction.objectStore("todos");

        newstore.openCursor().onsuccess = function (event) {
            const cursor = event.target.result;
            if (cursor) {
                if (cursor.value.ToDoTitle == 'TEST IDB 2') {       
                    const updateData = cursor.value;
                    updateData.ToDoCategory = 1; // hard coding for now
                    var requpdate = cursor.update(updateData);
                    requpdate.onsuccess = function () {
                        console.log('Updated');
                    };
                    requpdate.onerror = function () {
                        console.log('Error');
                    }
                };
                cursor.continue();
            } else {
                console.log('Cursor error.');
            }
        };
}

第一个警报会触发,但第二个不会。我假设因为返回了创建第一个事务的第一个回调,所以该事务已关闭,但它似乎仍然阻止我创建此事务。如果我完全取出第一个事务,则创建第二个事务并运行第二个警报......然后我可以从那里创建一个游标并更新数据。

我尝试将第一个事务和对象存储设为全局变量,但这也不起作用。

每页只让你执行一个事务似乎有点荒谬。我还想如何最初加载数据然后允许用户更新它?我错过了什么吗?

【问题讨论】:

  • UpdateToDo() 无法获得第二个警报,因为 update 未定义,这就是它不会触发第二个警报的原因。
  • 我在上面进行了编辑,以提供更大的代码块来查看我是如何进行更新的。我正在使用光标。但是,我不确定此时如何更新是否重要,因为我什至无法通过刚刚失败的第一条交易线。
  • 再次...如果我注释掉第一个事务/存储部分,第二个事务将完全触发,所以我假设有某些原因我无法创建第二个事务。我在文档中没有看到任何可以帮助我的内容
  • 我取出创建第二个事务/存储的行并将下一行从“newstore.openCursor().onsuccess”更改为使用全局存储“ostore.openCursor().onsuccess”,认为我可以重用我之前创建的对象存储。但是当我这样做时它在那里失败了。
  • 我也在学习如何使用 IndexedDB。我还不够了解,但我知道您必须启动一个新实例,例如为每个请求打开一个新连接,并且您的操作(无论是创建、读取、更新或删除)都需要是对请求的回调.基本上先发出请求然后函数(事件)并处理请求返回的事件。

标签: javascript indexeddb


【解决方案1】:

只有几点:

  • 不将alert 与 indexedDb 调用一起使用。 indexedDB 是异步的。 alert 不是异步的,它在显示时会暂停执行,这可能会导致一些奇怪的行为。最好使用 console.log,因为它不会停止执行。
  • 不要将执行 DOM 修改的代码和执行数据库查询的代码混合在同一个函数中(或嵌套)。我会将您的项目代码组织成只做一件事的更小的函数。
  • 未使用全局数据库变量。相反,每次都打开数据库。打开数据库一次,然后在点击时创建一个或多个事务是可行的,但我建议您根据需要每次都创建数据库连接。
  • 考虑使用一个事务。您可以在同一事务上同时发出读取和写入请求。
  • 如果您的部署环境支持,请考虑使用 async/await 和 Promise。如果操作正确,您的代码将变得非常易读。
  • 如果您只是更新一个值,请使用IDBObjectStore.prototype.get 而不是openCursor
  • 在读写 txn 中执行写入请求时,大多数情况下监听请求成功表示成功,但并非总是如此,因此最好监听 txn 完成事件。

例如,下面是一些遵循上述建议的伪代码:

function connect(name, version, upgrade) {
  return new Promise((resolve, reject) => {
    var req = indexedDB.open(name, version);
    req.onupgradeneeded = upgrade;
    req.onsuccess = event => resolve(req.result);
    req.onerror = event => reject(req.error);
  });
}

function onupgradeneeded(event) {
  var db = event.target.result;
  db.createObjectStore(...);
}

function updateTodo(db, todo) {
  return new Promise((resolve, reject) => {
    var tx = db.transaction('todos', 'readwrite');
    tx.onerror = event => reject(event.target.error);
    tx.oncomplete = resolve;
    var store = tx.objectStore('todos');
    var req = store.get(todo.title);
    req.onsuccess = event => {
      // When using get instead of openCursor, we just grab the result
      var old = event.target.result;

      // If we are updating we expect the current db value to exist
      // Do not confuse a get request succeeding with it actually matching 
      // something. onsuccess just means completed without error. 
      if(!old) {
        const error = new Error('Cannot find todo to update with title ' +  todo.title);
        reject(error);
        return;
      }

      // Change old todo object props
      old.category = todo.category;
      old.foo = todo.bar;

      // Replace old with a newer version of itself
      store.put(old);
   };
 });
}

function onpageloadInitStuff() {
  mybutton.onclick = handleClick;
}

async function handleClick(event) {
  var db;
  try {
    db = await connect('tododb', 1, upgradeneededfunc);
    var newtodo = todolist.querySelector('todolist[checked]');
    await updateTodo(db, newTodo);
  } catch(error) {
    alert(error); // it is ok to alert here, still better to console.log though
  } finally {
    if(db) {
      db.close();
    }
  }
}

【讨论】:

    【解决方案2】:

    Iskandar 和我同时有同样的想法。在 UpdateToDo 函数中,我打开了一个与 IndexedDB 实例的完全独立的连接,并使我的新事务/存储连接到该实例。处理该请求是可行的

        function UpdateToDo() {
        var udb = window.indexedDB.open("ToDoApp", 2);
        alert("1"); 
        udb.onsuccess = function (event) {
            alert("2");
            db2 = event.target.result;
            var trans2 = db2.transaction(["todos"], "readwrite");
            var ostore2 = trans2.objectStore("todos");
            alert("3");
            var blabla = ostore2.openCursor();
        blabla.onsuccess = function (event2) {    
    

    来自 MS Sql Server/ASP.NET 背景,打开到同一个数据库的多个连接感觉违反直觉。

    似乎 IndexedDB 和 Cache API 直到去年才在 Safari 上真正稳定,而且直到去年(2018 年)之前,Safari 甚至都没有包含服务工作者。虽然 Android 拥有全球市场,但在北美,如果 Apple 不支持它,就没有太多的开发......它有点解释了这些新功能的最后一个文档和教程。祝大家好运

    【讨论】:

    • >来自 MS Sql Server/ASP.NET 背景,打开到同一个数据库的多个连接感觉反直觉。 --- 是的,我自己花了很多时间来克服这个事实。糟透了,尽管 IndexedDB 没有其他选择。 WebSQL 已弃用,localStorage/sessionStorage 过于基础。
    • 我将继续我的研究,稍后再看。想你可能也想看看它:github.com/jensarps/IDBWrapper
    • dexie.org 是另一个 IDB 包装器。我看过一个很好的教程使用它。但是,我更喜欢使用直截了当的方式,直到我感觉足够舒服为止。然后我将继续使用包装器
    猜你喜欢
    • 2015-01-10
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2012-05-07
    • 1970-01-01
    • 2016-10-13
    • 1970-01-01
    相关资源
    最近更新 更多