使用 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/