如何在javascript中循环而不阻塞

时间:2013-08-01 11:13:05

标签: node.js

我有以下脚本

var email_list = ['email1@email.com', 'email2@email.com',....'email100@email.com'];
for(i=0;i<email_list.length;i++){
  if(checkEmail(email_list[i])){
    //do processing save in db and email to email addresses.
  }
}

此代码将在节点js中阻止如何使此非阻塞?

2 个答案:

答案 0 :(得分:3)

通过使用递归循环,您可以在不阻塞事件循环的情况下执行此操作。这样,您最终只能在给定时间每次调用启动一个数据库工作程序。假设您正在进行的数据库工作是异步的,那么您的代码并没有真正阻止事件循环。但是foor循环仍然同时启动了一堆工作程序,这将阻塞事件循环(不阻止它)。你是正确的,因为它阻止了事件循环,而你的for循环从0开始计数到数组的大小。以下内容完全相同,但您一次只启动一个数据库工作程序(好),并且从不计算从0到长度。在完成当前电子邮件的工作后,每个工作程序都会从​​列表中弹出,并且您的全局事件循环将处理其他内容,而不是同时处理email_list.length数据库请求。

var email_list = ['email1@email.com', 'email2@email.com', 'email100@email.com'];


function checkEmailList(emails, emailCallBack, completionCallback) {

    var someDataCollectdOverAllEmails = '';

    function checkEmailAsync(email) {
        db.doSomeDBWorkAsync(email, function (data) {

            someDataCollectdOverAllEmails += data;

            if (email_list.length) {
                checkEmail(email_list.pop()); //If there are still emails to be checked, check the next one ine line
            } else {
                completionCallback(someDataCollectdOverAllEmails);//IF not, call the completionCallBack
            }

            emailCallBack(data);
        });
    }

    checkEmailAsync(emails.pop());
}

function logIndividualEmailData(data) {
    console.log('Sningle Email: ' + data);
}

function logGlobalEmailData(data) {
    console.log('All Email Data: ' + data);
}

checkEmailList(email_list, logIndividualEmailData, logGlobalEmailData);

Process.nextTick示例

process.nextTick(function () {
    'use strict';
    console.log('printed second');
    while (true); 
});

process.nextTick(function () {
    'use strict';
    console.log('never printed');
});

console.log('printed first');

但请注意,在下面的示例中,尽管loopForever将永远运行,但它仍然允许读取我们的两个文件。如果我们只有while(true)它当然会阻止并且不允许这样,我们的一个文件数据将不会被打印出来。

var files = ['blah.js', 'file.js'];
for(var i = 0; i < files.length; i++) {
    fs.readFile(files[i], function (err, data) {
        console.log('File data' + data);

        function loopForver(loop) {//asynchronously loop forever, pretty cool, but only useful for really specific situations!
            process.nextTick(function () {
                if(loop) {
                    console.log('looping');
                    loopForver(true);
                }
            });
        }
        loopForver(true);
    });
}

答案 1 :(得分:2)

如果我需要在所有发送的电子邮件之后执行操作,我使用async库(docs),它为控制流提供了一些有用的功能。

您仍然需要将checkEmail(email)重写为checkEmail(email, callback)作为@ S.D。提示。在checkEmail中,您需要在完成所有操作后致电callback。这可能意味着您将嵌套回调,仅在第一个(db查询)成功完成后调用第二个异步(发送电子邮件)。 我还建议您使用第一个回调参数作为err参数来遵循约定。如果您callback(null)明确说'没有错误'。 @ S.D.的解决方案建议改为callback(ok),这与惯例相反。

这是一个示例,显示了几个嵌套的异步函数和async库。

编辑 - 使用async.eachLimit代替async.each,因此您不会同时执行所有100个调用

(function main(){
  var emails = ["a@b", "c@d"];
  var async = require('async');
  async.eachLimit(
    emails           // array to iterate across
   ,10               // max simultaneous iterations
   ,checkEmail       // an asynchronous iterator function
   ,function(err){   // executed on any error or every item successful
      console.log('Callback of async.eachLimit');
      if(err){ 
        console.log('Error: '+err) 
      } else {
        console.log('All emails succeeded');
      };  
    }   
  );  
  console.log('Code below the async.eachLimit call will continue executing after starting the asynchronous jobs');
})();

function checkEmail(email, callback){
  fetchFromDb(email, function(err, obj){
    if(err){ return callback(err) };
    sendEmail(email, function(err, obj){
      if(err){ return callback(err)};
      console.log('Both fetchFromDb and sendEmail have completed successfully for '+email);
      callback(null);
    }); 
  }); 
};

function fetchFromDb(email, callback){
  process.nextTick(function(){ // placeholder, insert real async function here
    callback(null);
  }); 
};

function checkEmail(email, callback){
  process.nextTick(function(){ // placeholder, insert real async function here
    callback(null);
  }); 
};