admin管理员组文章数量:1356243
I feel like this is a pretty mundane thing to do. I want to update an object in an IndexedDB database and then run some code after which uses the updated values.
What I originally did was just run my callback function after calling cursor.update
, which works in Firefox. But it fails in Chrome, the update doesn't happen before the following code is run. This is likely a race condition, since (as I understand it) updates are asynchronous.
So then I thought I should use the onsuccess
signal of cursor.update
to call my callback function. But much to my surprise, that also doesn't seem to work in Chrome!
Some example code which you can run on jsFiddle... although amusingly this seems to crash in Firefox in jsFiddle for some reason, but Chrome works fine; for Firefox you can run it locally and it works (this produces output on the JavaScript console in your browser):
<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>
Observed behavior (all on Ubuntu 12.10, FWIW):
In Firefox 19 (current stable version), all three logged objects are identical, with value
set to 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}
In Chrome 25 (current stable version) and 27 (current unstable version), I usually get this output:
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}
Sometimes one of the first two outputs is updated to 43, but it's usually a 42.
So again, my question is... how can I run something after the update is actually finished? (That is, without relying on some ridiculous arbitrary delay induced with setTimeout
.)
Alternative question: Am I doing something wrong, or is this a bug in Chrome?
Side question: If anyone has IE 10, I wonder how it behaves in this situation..
I feel like this is a pretty mundane thing to do. I want to update an object in an IndexedDB database and then run some code after which uses the updated values.
What I originally did was just run my callback function after calling cursor.update
, which works in Firefox. But it fails in Chrome, the update doesn't happen before the following code is run. This is likely a race condition, since (as I understand it) updates are asynchronous.
So then I thought I should use the onsuccess
signal of cursor.update
to call my callback function. But much to my surprise, that also doesn't seem to work in Chrome!
Some example code which you can run on jsFiddle... although amusingly this seems to crash in Firefox in jsFiddle for some reason, but Chrome works fine; for Firefox you can run it locally and it works (this produces output on the JavaScript console in your browser):
<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>
Observed behavior (all on Ubuntu 12.10, FWIW):
In Firefox 19 (current stable version), all three logged objects are identical, with value
set to 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}
In Chrome 25 (current stable version) and 27 (current unstable version), I usually get this output:
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}
Sometimes one of the first two outputs is updated to 43, but it's usually a 42.
So again, my question is... how can I run something after the update is actually finished? (That is, without relying on some ridiculous arbitrary delay induced with setTimeout
.)
Alternative question: Am I doing something wrong, or is this a bug in Chrome?
Side question: If anyone has IE 10, I wonder how it behaves in this situation..
Share Improve this question asked Mar 1, 2013 at 5:35 dumbmatterdumbmatter 9,7037 gold badges45 silver badges87 bronze badges 1- I think this was a Chrome bug. (Or maybe the spec has improved in the last 8 years?). According to this (especially the last bullet point in §2.7.2), the update transaction should plete (and the onsuccess event should fire, and the updated data should be stored) before either of the later two read transactions can start. – Doin Commented Mar 22, 2021 at 14:45
2 Answers
Reset to default 8You don't need setTimeout, just wait transaction to plete as follow:
// 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.onplete = 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);
};
}
This is one of the confusing aspect of IndexedDB API. Request onsuccess don't not mean your success is written to the database. Only transaction onplete confirm it. The reason is, you can still abort transaction tx.abort()
after write request.
I used the promise pattern to avoid the settimeout and JayData library to unify the data access and hide cursor API. After including the library (include.jaydata/jaydata.min.js) the snippet looks like this (online jsfiddle):
$data.Entity.extend("Todo", {
Id: { type: "int", key: true, puted: 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);
});
})
});
It's much less code and you only have to change the type of the provider to change to WebSQL :)
版权声明:本文标题:javascript - Running code only after an object is updated in IndexedDB (particularly in Chrome) - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744037316a2580015.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论