我觉得这是一件很平凡的事情。我想在IndexedDB数据库中更新一个对象,然后运行一些代码,之后使用更新的值。
我最初做的只是在调用cursor.update
后运行我的回调函数,它在Firefox中工作。但它在Chrome中失败,更新不会发生在以下代码运行之前。这可能是一个竞争条件,因为(据我所知)更新是异步的。
所以我想我应该使用cursor.update
的onsuccess
信号来调用我的回调函数。但令我惊讶的是,这在Chrome中似乎也不起作用!
一些可以在jsFiddle上运行的示例代码…虽然有趣的是,这似乎崩溃在Firefox在jsFiddle的某种原因,但Chrome工作得很好;对于Firefox,您可以在本地运行它,并且它可以工作(这会在浏览器的JavaScript控制台中产生输出):
<html>
<head>
<script>
var db, request;
request = indexedDB.open("test", 1);
request.onupgradeneeded = function (event) {
var i, leagueStore, teams, teamStore;
db = event.target.result;
objectStore = db.createObjectStore("objects", {keyPath: "id"});
};
request.onsuccess = function (event) {
db = request.result;
// Add some dummy data
db.transaction("objects", "readwrite").objectStore("objects").put({
id: 0,
value: 42
});
// Update data
db.transaction("objects", "readwrite").objectStore("objects").openCursor(0).onsuccess = function (event) {
var cursor, object;
cursor = event.target.result;
object = cursor.value;
object.value = 43;
cursor.update(object).onsuccess = function (event) {
db.transaction("objects").objectStore("objects").get(0).onsuccess = function (event) {
console.log("Cursor update onsuccess event:");
console.log(event.target.result);
};
};
// Read back updated data
db.transaction("objects").objectStore("objects").get(0).onsuccess = function (event) {
console.log("The line after the cursor update:");
console.log(event.target.result);
};
// Wait a little bit, then read it back
setTimeout(function () {
db.transaction("objects").objectStore("objects").get(0).onsuccess = function (event) {
console.log("After an additional delay via setTimeout:");
console.log(event.target.result);
};
}, 100);
};
};
</script>
</head>
</html>
观察到的行为(全部在Ubuntu 12.10, FWIW上):
在Firefox 19(当前稳定版本)中,所有三个记录对象都是相同的,value
设置为43:
The line after the cursor update:
Object {id: 0, value: 43}
Cursor update onsuccess event:
Object {id: 0, value: 43}
After an additional delay via setTimeout:
Object {id: 0, value: 43}
在Chrome 25(当前稳定版本)和27(当前不稳定版本),我通常得到这个输出:
The line after the cursor update:
Object {id: 0, value: 42}
Cursor update onsuccess event:
Object {id: 0, value: 42}
After an additional delay via setTimeout:
Object {id: 0, value: 43}
有时前两个输出中的一个被更新为43,但通常是42。
我的问题是……我如何在更新实际完成后运行一些东西?(也就是说,不依赖于setTimeout
引起的一些荒谬的任意延迟。)
替代问题:我做错了什么,或者这是一个bug在Chrome?
附带问题:如果有人有ie10,我想知道它在这种情况下的行为…
您不需要setTimeout,只需等待事务完成,如下所示:
// Update data
var tx = db.transaction("objects", "readwrite");
tx.objectStore("objects").openCursor(0).onsuccess = function (event) {
var cursor, object;
cursor = event.target.result;
object = cursor.value;
object.value = 43;
cursor.update(object).onsuccess = function (event) {
db.transaction("objects").objectStore("objects").get(0).onsuccess = function (event) {
console.log("Cursor update onsuccess event:");
console.log(event.target.result);
};
};
};
tx.oncomplete = function() {
// Read back updated data
db.transaction("objects").objectStore("objects").get(0).onsuccess = function (event) {
console.log("The line after the cursor update:");
console.log(event.target.result);
};
}
这是IndexedDB API令人困惑的一个方面。请求成功并不意味着你的成功被写入数据库。只有交易完成后才能确认。原因是,在写请求之后,仍然可以中止事务tx.abort()
。
我使用promise模式来避免settimeout和JayData库来统一数据访问和隐藏游标API。在包含库(include.jaydata.org/jaydata.min.js)之后,代码片段看起来像这样(online jsfiddle):
$data.Entity.extend("Todo", {
Id: { type: "int", key: true, computed: true },
Task: { type: String, required: true, maxLength: 200 }
});
$data.EntityContext.extend("TodoDatabase", {
Todos: { type: $data.EntitySet, elementType: Todo }
});
var todoDB = new TodoDatabase({
provider: 'indexedDb', databaseName: 'MyTodoDatabase'
});
todoDB.onReady(function() {
var newTodo = new Todo({Task: "alma"});
todoDB.Todos.add(newTodo);
todoDB.saveChanges()
.then(function() {
console.log("Initial value: ", newTodo.Task);
todoDB.Todos.attach(newTodo);
newTodo.Task = "korte";
return todoDB.Todos.saveChanges();
})
.then(function(){
console.log("Updated value: ", newTodo.Task);
return todoDB.Todos
.filter(function(t) {return t.Id == item.Id;}, {item: newTodo})
.toArray(function(dbResult){
var todoFromDb = dbResult[0];
console.log("Value from DB: ", todoFromDb.Task);
});
})
});
代码少得多,你只需要改变提供者的类型,改成WebSQL:)