在Node js中完成for循环时调用函数

时间:2015-04-16 17:39:26

标签: javascript node.js function for-loop

function inboxUsers(){
    for (var i=0; i<uniqueArray.length; i++){
        var getUsername    = 'SELECT userName FROM users WHERE userId = ' + uniqueArray[i];
        db.query(getUsername, function(err, results) {
            if (err) {
                console.log('Error in database');
                throw err;
            }
            for(var i in results){
                console.log('single',results[i].userName);
                inboxUserList.push(results[i].userName);
            }
        });
    }
    sample();
}

function sample(){
    console.log('same function');
}

这是我的控制台输出。

same function
single user1
single user2
single user3

在这段代码中,我在for循环之后调用了函数 sample(),但是在循环结束之前它调用了 sample()函数

我想在for循环结束时调用sample()函数。我是stackoverflow的初学者,如果我有错误请向我道歉。谢谢

2 个答案:

答案 0 :(得分:5)

您对db.query的呼叫是异步的。这意味着什么:

  1. db.query(...)的调用会立即返回,不会返回任何内容。

  2. 不是将返回值赋给变量(var results = db.query(...)),而是传递callback function作为参数,以便db模块可以在完成提取时调用结果。它将挂起到回调函数,直到数据库有结果,然后它会在函数准备就绪时调用它。

  3. 因为对db.query(...)的调用立即返回,所以for循环将完成,并且在您提供给查询的回调函数被db模块调用之前将调用sample()


  4. 要确保在完成所有通话后sample运行,您需要跟踪每个查询的完成情况,然后在所有查询都返回后触发sample函数。在我看来,最简单的方法是在不向您介绍复杂主题的情况下执行此操作,例如&#34; promises&#34;,使用名为async的模块及其parallel方法。

      

    $ npm install async --save

    var async = require('async');
    var queries = [];
    
    function inboxUsers(){
      uniqueArray.forEach(function (userId) {
        var getUsername = 'SELECT userName FROM users WHERE userId = ' + userId;
        queries.push(function (done) {
          db.query(getUsername, done);
        });
      });
      async.parallel(queries, function (err, allQueryResults) {
        if (err) { return console.error(err); }
        allQueryResults.forEach(function (queryResults) {
          queryResults.forEach(function (result) {
            console.log('single', result.userName);
            inboxUserList.push(result.userName);
          });
        });
        sample();
      });
    }
    
    function sample(){
      console.log('same function');
    }
    

    这里又是一次,但采用了更少的快捷方式和详细的评论。

    var async = require('async');
    
    // create an array to store a bunch of functions that the async library
    // should fire and wait to finish.
    var queries = [];
    
    function inboxUsers(){
      uniqueArray.forEach(function (userId) {
        var getUsername = 'SELECT userName FROM users WHERE userId = ' + userId;
        var queryFunc = function (done) {
          db.query(getUsername, function(err, results) {
            // let the async lib know this query has finished.
            // the first argument is expected to be an error.
            // If the err is null or undefined then the async lib
            // will ignore it. The second argument should be our results.
            done(err, results);
          });
    
          // You could make the above even simpler by just passing
          // the done function as the callback to db.query. I just
          // didn't want to confuse you by doing that.
          // db.query(getUsername, done);
        };
        queries.push(queryFunc);
      });
      // Fire all async functions by passing in our queries array.
      // The async library will wait for them all to call "done()" 
      // before it invokes this final function below.
      async.parallel(queries, function (err, allQueryResults) {
        // If any of our queries pass an error to "done" then the async
        // lib will halt the rest of the queries and immediately invoke
        // this function, passing in the error.
        if (err) { return console.error(err); }
    
        // queryResults is an array containing the results of each query we made.
        allQueryResults.forEach(function (queryResults) {
          queryResults.forEach(function (result) {
            console.log('single', result.userName);
            inboxUserList.push(result.userName);
          });
        });
    
        // All your queries are complete and your inboxUserList array
        // is populated with the data you were after. Now we can call
        // "sample".
        sample();
      });
    }
    
    function sample(){
      console.log('same function');
    }
    

    async lib知道你向数组提供了多少函数,因此它知道在调用最终函数之前它应该等待多少次done

答案 1 :(得分:1)

您很可能遇到此问题,因为您的db.query()功能不同步。它需要一个可以在完成后调用的回调函数。

在数据库库查询数据库并获得结果之前,不会调用代码inboxUserList.push(...)。同时,你的for循环将继续运行,准备好所有查询并在完成之前继续。然后调用sample(),因为for循环已完成,即使您传入的回调尚未被调用。

有许多解决方案,但最简单的当前代码可能是这样的:

function inboxUsers(){
    var completed = 0;
    for (var i=0; i<uniqueArray.length; i++){
        var getUsername    = 'SELECT userName FROM users WHERE userId = ' + uniqueArray[i];
        db.query(getUsername, function(err, results) {
            if (err) {
                console.log('Error in database');
                throw err;
            }
            for(var i in results){
                console.log('single',results[i].userName);
                inboxUserList.push(results[i].userName);
            }

            completed++;
            if (completed == uniqueArray.length) {
                sample();
            }
        });
    }
}

function sample(){
    console.log('same function');
}