簡體   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