4

我有以下脚本

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.
  }
}

这段代码将在nodejs中阻塞如何使这个非阻塞?

4

2 回答 2

4

通过使用递归循环,您可以在完全不阻塞事件循环的情况下做到这一点。这样,您最终得到的只是每次调用在给定时间启动一个数据库工作者。假设您正在执行的数据库工作是异步的,那么您的代码并没有真正阻塞事件循环。但是 foo 循环仍然同时启动了一堆工作人员,这往往会阻塞事件循环(而不是阻塞它)。你是对的,它阻塞了事件循环,而你的 for 循环从 0 计数到数组的大小。以下内容完全相同,但您一次只启动一个数据库工作程序(好),并且您永远不会从 0 数到长度。在当前电子邮件的工作完成后,每个工作人员都会从列表中弹出,并且您的全局事件循环将留给处理其他事情,而不是 email_list。

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);
    });
}
于 2013-08-01T13:37:23.653 回答
2

如果我需要在电子邮件全部发送后做一些事情,我会使用async库 ( docs ),它为控制流提供了一些有用的功能。

您仍然需要按照@SD 的建议checkEmail(email)重写。checkEmail(email, callback)在一切都完成后,checkEmail你会想要打电话。callback这可能意味着您将嵌套回调,仅在第一个(数据库查询)成功完成后调用第二个异步事物(发送电子邮件)。我还建议您通过使用第一个回调参数作为err参数来遵循约定。如果您callback(null)明确表示“没有错误”。@SD 的解决方案建议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);
  }); 
};
于 2013-08-01T13:44:24.850 回答