JS等待回调在循环内完成

时间:2014-07-03 01:31:29

标签: javascript asynchronous

我在我的节点js app中有一个for循环;在每次迭代的循环中,可以执行一个mysql查询(并不总是依赖);查询是异步的,我在成功回调中得到结果;但是我需要for循环的每次迭代都等待回调完成(如果需要):

function calculate() {
    var resArray = [];

    //[...]

    for (i = 0; i < tables.length; i++) {
        if (id !== undefined) {
            var queryString = ...  //build query
            sequelize.query(queryString).success(function(result) {   //executes query
                id = result[0].id;

                //do stuff with id returned by the query
                //...
                //resArray.push( //push in resArray the result of stuff done above )
            });  
       }
       else {
           //do other kind of stuff and push result in resArray
       }
   }
   return resArray;
}

如果id!= undefined,则执行查询,但for循环不等待成功回调,函数返回不完整的resArray。

我该怎么做(或者我如何重新组织代码)才能使其工作? (我正在使用节点js,我没有机会使用jQuery)。

2 个答案:

答案 0 :(得分:2)

感谢Amadan建议我使用混合方法(使用promises和counter)摆脱了这个;

我真的对您对此的看法感兴趣,如果您有改进建议,

首先,我在项目中添加了q库(npm install q)以使用promises。 感谢q我已经能够在返回promise的函数中包装sequelize.query(基于回调); promises的计数器在内存中保留了仍然未决的promise数量,并且包装函数executeQuery在每个promise都被解析时会对它进行下限;

(cycle()函数中的for循环执行4个循环;它执行2次查询,2次执行简单日志; 目标是只有在循环结束并且所有承诺都已解决之后,“已完成”的日志才会被发送到屏幕

var id = req.params.id;
var counter = 0;
var endCycle = false;

var queryString = 'SELECT * FROM offers WHERE id = ' + id;

function query () {
    var deferred = Q.defer();
    sequelize.query(queryString).success(function(result) {
        console.log('obtained result');
        deferred.resolve(result);
    });
    return deferred.promise;
}

function executeQuery() {
    query().then(function() {
        console.log('after');
        counter --;
        console.log(counter);
        finished();
    });
}

function finished() {
    if ((counter === 0) && (endCycle)) {
        console.log('finished');
        endCycle = false;
    }
}

function cycle() {
    var result;

    for (i = 0; i <= 3; i ++) {
        if (i > 1) {
            counter ++;
            executeQuery();
        }
        else {
            console.log('else');
        }
    }

    endCycle = true;
    finished();
}

cycle();

------------------ UPDATE ---------------------------- --------------------------------

按照hugomg的建议我已经用更干净的代码更新了代码: 我在数组中推送每个promise,然后使用Q.all等待所有这些解析

var id = req.params.id;
var promisesArray = [];
var endCycle = false;

var queryString = 'SELECT * FROM offers WHERE id = ' + id;
function query () {
    var deferred = Q.defer();
    sequelize.query(queryString).success(function(result) {
        console.log('obtained result');
        deferred.resolve(result);
        finished();
    });
    promisesArray.push(deferred.promise);
}

function finished() {
    if (endCycle) {
        endCycle = false;
        Q.all(promisesArray).then(function() {
            console.log('finished');
        });            
    }
}

function cycle() {
    var result;

    for (i = 0; i <= 3; i ++) {
        if (i > 1) {
            query();
        }
        else {
            console.log('else');
        }
    }
    endCycle = true;
    finished();
}

cycle();

答案 1 :(得分:1)

你需要改变你的思维方式。如果calculate调用异步方法,则获取结果的唯一方法不是return,而是通过调用另一个回调。在node.js中,你不能把一个函数想象成一个接收输入并返回输出的黑盒子;你需要把它看作是一个有一定未来的过程中的一步。你需要问自己的问题是 - &#34;我得到了这些结果;那么呢?&#34;

就像使用sequelize.query一样,你告诉它&#34;做这个查询,然后用结果&#34; 这个,你需要能够调用{ {1}}然后告诉它&#34;去做那些查询,然后用他们的结果&#34; 这个。您未能从calculate获得退货 - 而且您也不应该尝试从sequelize.query返回某些内容。

承诺会很有效,但如果不依赖它们,你可以算一算。下面的代码有四处变化:

calculate

然后你可以替换假设和非功能

// HERE: take a callback parameter
function calculate(callback) {
    var outstandingRequests = 0;
    var resArray = [];

    //[...]

    for (i = 0; i < tables.length; i++) {
        if (id !== undefined) {
            var queryString = ...  //build query

            // HERE: count how many responses you are expecting
            outstandingRequests++;

            sequelize.query(queryString).success(function(result) {   //executes query
                id = result[0].id;

                //do stuff with id returned by the query
                //...
                //resArray.push( //push in resArray the result of stuff done above )

                // HERE: check if all requests are done
                // if so, the array is as full as it will ever be
                // and we can pass the results on
            }).done(function() {
                if (!(--outstandingRequests)) {
                    callback(resArray);
                }
            });  
       }
       else {
           //do other kind of stuff and push result in resArray
       }
   }
   // HERE: return is useless in asynchronous code
}

var resArray = calculate();
console.log("Here's your results:", resArray);

编辑:将倒计时放入calculate(function(resArray) { console.log("Here's your results:", resArray); }); 处理程序以解决可能的错误。