javascript - IndexedDB w/两个事务 : 1 read then 1 update

标签 javascript indexeddb

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

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

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.');
            }
        };
}

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

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

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

最佳答案

简单几点:

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

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

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();
    }
  }
}

关于javascript - IndexedDB w/两个事务 : 1 read then 1 update,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54243024/

相关文章:

javascript - Angular : Is sharing a Controller between views a sign that Service is needed?

javascript - 仅在安装时通过 WhatsApp 共享

javascript - Angular $scope 对象显示 $scope.data 但当我尝试使用它时,显示未定义

sqlite - PouchDB/SQLite索引崩溃-需要IOS上的大量内存

dart - IndexedDb:database.close() 挂起

html - 在 IndexedDB 中,有没有办法进行排序复合查询?

javascript - indexedDB idb 库 - 插入多条记录

javascript - 索引 : Select and Insert 的 IndexedDB 性能

JavaScript 在 iMacros (JQuery) 中不起作用

javascript - 在angularjs中将字符串转换为对象时出错