具有两个事务的IndexedDB:1个读取然后1个更新

时间:2019-01-17 19:30:48

标签: javascript indexeddb

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

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

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

只让您每页执行一次事务似乎有些荒谬。我还应该如何初始加载数据,然后允许用户对其进行更新?我想念什么吗?

2 个答案:

答案 0 :(得分:2)

几点:

  • 不将alert与indexdDb调用一起使用。 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();
    }
  }
}

答案 1 :(得分:1)

伊斯坎达和我在同一时间有相同的想法。在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背景,打开与同一个DB的多个连接感觉很直观。

直到去年,IndexedDB和Cache API似乎在Safari上才真正稳定,直到去年(2018年),服务人员才完全没有包含在Safari中。尽管Android拥有全球市场,但在北美,如果Apple不支持Android,则不会有太多的发展……它解释了这些新功能的最后文档和教程。祝大家好运