繁体   English   中英

JS等待回调在循环内完成

[英]JS wait for callback to finish inside a loop

我的节点js应用程序中有一个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)。

多亏了阿玛丹(Amadan)的建议,我使用了混合方法(带有保证和反击)来摆脱了这种情况。

我真的很感兴趣您对此的意见,以及您是否有改进建议。

首先,我已将q库(npm install q)添加到项目中以使用promises。 多亏了q,我才能够将sequelize.query(基于回调)包装在返回promise的函数中; 一个promise计数器将仍在等待中的promise数量保留在内存中,并且包装函数executeQuery在解析每个promise时将其减少;

(for cycle in cycle()函数执行4个循环;它执行2次查询和2次简单日志;目标是“完成的”日志仅在for循环结束且所有promise均被解析后才发送到屏幕

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();

------------------更新------------------------------- -----------------------------

遵循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();

您需要改变思维方式。 如果calculate调用异步方法,则获取其结果的唯一方法不是通过return ,而是通过调用另一个回调。 在node.js中,您不应将函数视为一个接受输入并返回输出的黑盒子。 您需要将其视为具有一定前途的过程中的一步。 您需要问自己的问题是-“我得到了这些结果;然后呢?”

就像sequelize.query ,您要告诉它“执行此查询,然后对结果执行此操作 ”,您需要能够调用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);
});

编辑:将倒数放入done处理程序中以解决可能的错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM