使用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.');
}
};
}
第一个警报会触发,但第二个警报不会触发。我以为是因为返回了创建第一个事务的第一个回调,所以该事务已关闭,但是它似乎仍然阻止我创建此事务。如果我完全完成了第一个事务,则创建第二个事务,并运行第二个警报...然后,我可以从那里创建一个游标并更新数据。
我尝试使第一个事务和对象存储作为全局变量,但这也不起作用。
只让您每页执行一次事务似乎有点荒谬。我还应该如何初始加载数据,然后允许用户对其进行更新?我想念什么吗?
最佳答案
请注意以下几点:
不能将alert
与indexedDb调用一起使用。 indexedDB是异步的。 alert
不是异步的,它在显示时暂停执行,这可能导致某些奇怪的行为。最好使用console.log,因为这不会停止执行。
不能将执行DOM修改的代码与执行数据库查询的代码混合在同一函数(或嵌套函数)中。我会将您的项目代码组织成只做一件事的较小函数。
不使用全局数据库变量。而是,每次打开数据库。一次打开数据库,然后单击创建一个或多个事务都可以,但是我建议您每次根据需要创建数据库连接。
考虑使用一次交易。您可以在同一事务上发出读取和写入请求。
如果您的部署环境支持,请考虑将async / await与promise一起使用。如果正确完成,您的代码将变得非常易读。
如果您只是更新一个值,请使用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();
}
}
}