Javascript IndexedDB包含两个事务:1个读取,1个更新

Javascript IndexedDB包含两个事务:1个读取,1个更新,javascript,indexeddb,Javascript,Indexeddb,使用IndexedDB,我似乎无法在已经创建了用于读取和显示数据的事务之后,为objectstore创建第二个事务(只是ToDo的列表)。我没有在尝试创建第二个事务的行中得到错误,但是什么都没有发生,代码停止执行 检索和显示我的数据的此部分运行良好: var trans; var ostore; var db; var reqdb = window.indexedDB.open("ToDoApp", 2); reqdb.onupgradeneeded = function (event) {

使用IndexedDB,我似乎无法在已经创建了用于读取和显示数据的事务之后,为objectstore创建第二个事务(只是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);
    };
};
这可以很好地获取和显示内容。但是请注意UpdateToDo()函数,它通过onclick事件进行设置,因此我可以实际更新数据

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函数中,我打开了一个与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在全球市场占有一席之地,但在北美,如果苹果不支持它,就不会有太多的发展。。。它在某种程度上解释了这些新功能的最后一个文档和教程。祝大家好运

只要几点:

  • 不将
    警报
    与indexedDb调用一起使用。indexedDB是异步的<代码>警报不是异步的,它会在显示时停止执行,这可能会导致一些奇怪的行为。最好使用console.log,因为这不会停止执行
  • 不要在同一个函数(或嵌套函数)中混合执行DOM修改的代码和执行数据库查询的代码。我会把你的项目代码组织成更小的函数,只做一件事
  • 不使用全局数据库变量。而是每次打开数据库。打开数据库一次,然后单击创建一个或多个事务可以工作,但我建议您根据需要每次创建数据库连接
  • 考虑使用一个事务。您可以在同一事务上发出读取和写入请求
  • 如果您的部署环境支持异步/等待,请考虑将其与承诺一起使用。如果操作正确,您的代码将变得非常可读
  • 如果您只是更新一个值,请使用
    IDBObjectStore.prototype.get
    而不是
    openCursor
  • 在readwrite 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();
    }
  }
}

UpdateToDo()
无法进入第二个警报,因为未定义
update
,这就是为什么它不会触发第二个警报。我在上面进行了编辑,以提供更大的代码块,以查看我如何进行更新。我用的是光标。然而,我甚至不确定在这一点上我如何更新,因为我甚至无法通过刚刚失败的第一个事务行。再次。。。如果我注释掉第一个事务/存储部分,那么第二个事务将完全启动,因此我假设有某种原因我无法创建第二个事务。我在文档中没有看到任何东西可以帮助meI删除创建第二个事务/存储的行,并将下一行从“newstore.openCursor().onsuccess”更改为使用全局存储“ostore.openCursor().onsuccess”,认为我可以继续使用我先前创建的对象存储。但当我这么做的时候,它就失败了。我也只是在学习如何使用IndexedDB。我还不太清楚,但我知道您必须启动一个新实例,就像为每个请求打开一个新连接一样,您的操作(无论是创建、读取、更新还是删除)都需要是对请求的回调。基本上,先发出请求,然后运行(事件)并处理请求返回的事件。>来自MS Sql Server/ASP.NET后台,打开到同一数据库的多个连接感觉很不直观。--是的,我自己花了很多时间来克服这个事实。可惜的是,除了IndexedDB没有其他选择。WebSQL已被弃用,localStorage/sessionStorage太基础了。我将继续我的研究,稍后再看。我想您可能也想看看它:是另一个IDB包装器。我看过一个很好的教程使用这个。然而,我更喜欢用直截了当的方式去做,直到我感觉足够舒服为止。然后我将继续讨论包装