我有以下脚本
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中阻止如何使此非阻塞?
答案 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);
});
};