Javascript中的回调执行序列,从IndexeddB检索

Callback execution sequence in Javascript, Retrieving from IndexeddB

本文关键字:IndexeddB 检索 回调 执行 Javascript      更新时间:2023-09-26

如果我有这样的代码:

testJSCallbacks();
function testJSCallbacks(){
    var i = 0;
    for (i = 0; i < 5; i++){
        console.log("Step 1 "+i);
        foo(i, myCB);
    }
}
function foo(key, fnCB){
    //retrieve png image blob from indexedDB for the key 'key'. Assume that the database is
    //created and started properly
    var getRequest = transaction.objectStore("store").get(key);
    getRequest.onsuccess = function (event) {
        var result = event.target.result;
        if(result){
            console.log("Step 2 " + key + " Found");
        }else{
            console.log("Step 2 " + key + " not Found");    //for the same 'key' value this happens before the above result is valid. i.e. key found
        }
        fnCB(result);
    }
}
myCB = function (result){
    console.log("Step 3 "+result);
}

实际输出(仅举例):

Step 1 0
Step 1 1
Step 1 2
Step 1 3
Step 1 4
Step 2 0 Found
.
.
Step 3 <result>
.
.
.
所需输出:

 Step 1 0
 Step 2 0 Found
 Step 3 <result value of key 0 goes here>

在我的代码中,我试图从IndexedDB读取png blobs,这已经存储在前面。但是,当读取/搜索特定blob时,需要很长时间才能获得结果,同时,即使先前的搜索尚未完成,也会发生对第二个blob的搜索。

如果你需要在循环中多次调用异步函数并且回调需要很长时间,谁能建议你做什么/如何做?我的代码是正确的,有逻辑意义,或者这不是javascript是怎么做的?我是一个嵌入式C背景的新手。

问题是getRequest。Onsuccess函数是异步的,而for循环是同步执行的。这就是为什么它会先结束…实际上,当您执行testJsCallbacks时,在当前执行上下文结束并将控制返回给javascript事件队列之前不会执行任何其他操作,因为浏览器中的javascript执行上下文是单线程的。

要做你想做的,我建议使用promise库。然后你可以写这样的代码(参见使用Q.js库的jsfiddle):

testJSCallbacks();
function testJSCallbacks(){
    var i = 0,
        promise;
    for (i = 0; i < 5; i++) {
        //Make initial promise if one doesn't exist
        if (!promise) {
            promise = Q.fcall(getStep(i));
        }
        //Append to existing promise chain
        else {
            promise = promise.then(getStep(i));
        }
        //then function returns another promise that can be used for chaining.
        //We are essentially chaining each function together here in the loop.
        promise = promise.then(function (key) {
            //Log the output of step here
            console.log("Step 1 " + key);
            return key;
        })
        //then function takes a callback function with one parammeter (the data).
        //foo signature meets this criteria and will use the resolution of the last promise (key).
        .then(foo)
        //myCB will execute after foo resolves its promise, which it does in the onsuccess callback
        .then(myCB);
    }
}
function getStep(step) {
    return function () {
        return step;
    }
}
function foo(key) {
    //retrieve png image blob from indexedDB for the key 'key'. Assume that the database is
    //created and started properly
    var getRequest = transaction.objectStore("store").get(key),
        //Need to return a promise
        deferred = Q.defer();
    getRequest.onsuccess = function (event) {
        var result = event.target.result;
        if(result){
            console.log("Step 2 " + key + " Found");
        }else{
            console.log("Step 2 " + key + " not Found");    //for the same 'key' value this happens before the above result is valid. i.e. key found
        }
        deferred.resolve(result);
    }
    return deferred.promise;
}
function myCB (result){
    console.log("Step 3: " + result);
}

jsfiddle使用setTimeout而不是objectStore来演示异步特性。

说明getStep函数:

getStep函数就像一个"种子"函数,它开始解析你想要做的事情的链(即步骤1,步骤2,步骤3)。它只是创建一个返回传入变量值的函数。这是用来传入控制台的函数。在承诺解析链中记录步骤1,然后返回下一个承诺解析的值(步骤2)…JavaScript有闭包的概念,为了获得步数的正确值(而不是执行回调时的'i'的值),我们需要为变量i创建一个闭包。

为了演示,考虑以下代码:HTML:

<button type="button">0</button>
<button type="button">1</button>
<button type="button">2</button>
<button type="button">3</button>
<button type="button">4</button>
addHandlers();
function addHandlers() {
    //Don't do this. Just demonstrating a feature:
    var buttons = document.getElementsByTagName("button") || [],
        i, len = buttons.length;
    for (var i = 0; i < len; i++) {
        buttons[i].onclick = function () {
            //will always alert 5
            alert(i);
        }
    }
}

由于变量i在for循环结束后为5,因此这是函数中使用的值。这就是为什么你需要为i创建一个闭包(为了清晰起见,再次使用getStep):

addHandlers();
function addHandlers() {
    var buttons = document.getElementsByTagName("button") || [],
        i, len = buttons.length;

    for (var i = 0; i < len; i++) {
        //getStep creates a function with a closure for i at its current value in the loop
        buttons[i].onclick = getStep(i);
    }
}
function getStep(i) {
    return function () {
            alert(i);
        }
}

查看前后