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

Posted

技术标签:

【中文标题】在Node js中完成for循环时调用函数【英文标题】:Call function when for loop finished in Node js 【发布时间】:2015-06-23 07:10:39 【问题描述】:
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(),但它在 for 循环结束之前调用了 sample() 函数

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

【问题讨论】:

欢迎来到 StackOverfow!请更新您的问题以将输出包含到控制台或以其他方式描述您如何知道在循环结束之前正在调用该函数示例。 for 循环将始终在 sample 运行之前完成;但是,db.query 似乎是异步的,这意味着 sample 将在您传递给它的回调之前运行。 @dgvid 我更新了我的问题。这些包括控制台输出 在下面查看我的答案。这是一个非常常见的异步场景,可以轻松修复:) 【参考方案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');

【讨论】:

我收到了这个错误,TypeError: undefined is not a function at inboxUsers (C:\Users\yathu\IdeaProjects\chatting\app.js:148:16),这是第 148 行:).then(function() @user3702886 db 使用的是什么库? var mysql = require('mysql'); 在这种情况下,您甚至不需要使用.then.on。只需将completed++ 和下面的if 语句向上移动到for 循环下方的查询回调函数中。然后,您可以完全避免使用 Promise 和事件处理程序,这对于初学者来说有些复杂。 @AlexFord 这点很好!我简化了我的例子来反映这一点。【参考方案2】:

您对db.query 的调用是异步的。这是什么意思:

    db.query(...) 的调用立即返回,什么也不返回。

    您无需将返回值分配给变量 (var results = db.query(...)),而是将 callback function 作为参数传递,以便 db 模块在完成获取结果时可以调用。它会挂在回调函数上,直到数据库有你的结果,然后它会在它准备好时调用该函数。

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


为确保在所有调用完成后sample 运行,您需要跟踪每个查询的完成情况,然后在所有查询返回时触发sample 函数。在我看来,不向您介绍“承诺”等复杂主题的最简单方法是使用名为 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');


又来了,但使用的快捷方式更少,cmets 更详细。

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 库知道您向数组提供了多少函数,因此它知道在调用最终函数之前应该等待多少次对 done 的调用。

【讨论】:

这条鳕鱼在数组中推送相同的用户名,我在 sample() 中打印 inboxUserList,我得到了这个结果[ 'kabilan', 'kabilan', 'kabilan' ] 我还将for (var i=0; i&lt;uniqueArray.length; i++) 更改为uniqueArray.forEach(function (userId) 。这将确保每次迭代都保留该特定查询的唯一 userId。我再次更新了答案以显示该更改。它现在应该可以工作了;如果您遇到更多问题,请告诉我:) console.log('single', result.userName); 这一行打印 Undefiend 我无法识别示例中的任何错误,也无法测试它,因为我没有您正在运行的完整代码。我首先检查传递给async.parallel 回调(allQueryResults)的内容,然后看看它是什么样子的。它应该是一个数组数组。每个嵌套数组都是相关查询的结果。试试console.log(result); 看看result 是什么。如果它不是您所期望的,那么我们可以从那里找出原因。

以上是关于在Node js中完成for循环时调用函数的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 等到异步函数完成,然后继续执行其余代码

Node.js 等到异步函数完成,然后继续执行其余代码

当循环中调用了异步函数时,Node.JS 将如何处理循环控制?

node.js 回调获取变量的意外值

在 Bootstrap 模式中单击按钮时调用 JS 函数

当 Dojo 自动完成器的值改变时调用 java-script 函数