3

これはかなり平凡なことだと思います。IndexedDBデータベースのオブジェクトを更新してから、更新された値を使用するコードを実行したいと思います。

私が最初にしたことはcursor.update、Firefoxで動作するを呼び出した後にコールバック関数を実行することでした。ただし、Chromeでは失敗します。次のコードが実行される前に更新は行われません。(私が理解しているように)更新は非同期であるため、これは競合状態である可能性があります。

そこで、のonsuccessシグナルを使用しcursor.updateてコールバック関数を呼び出す必要があると思いました。しかし、驚いたことに、それはChromeでも機能しないようです。

jsFiddleで実行できるいくつかのサンプルコード...面白いことに、これは何らかの理由でjsFiddleのFirefoxでクラッシュするようですが、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(現在の安定バージョン)では、ログに記録された3つのオブジェクトはすべて同一であり、value43に設定されています。

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}

最初の2つの出力の1つが43に更新されることがありますが、通常は42です。

繰り返しになりますが、私の質問は...更新が実際に終了した後に何かを実行するにはどうすればよいですか?(つまり、で引き起こされるばかげた任意の遅延に依存することなくsetTimeout。)

別の質問:私は何か間違ったことをしていますか、それともこれはChromeのバグですか?

副次的な質問:IE 10を持っている人がいたら、この状況でどのように動作するのだろうか。

4

2 に答える 2

8

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 の紛らわしい側面の 1 つです。リクエストが成功しても、成功がデータベースに書き込まれるわけではありません。取引完了のみご確認下さい。tx.abort()その理由は、書き込み要求の後でもトランザクションを中止できるからです。

于 2013-03-01T10:40:12.563 に答える
0

プロミス パターンを使用して 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に変更するだけです:)

于 2013-03-01T09:19:24.933 に答える