回调地狱在nodejs? [英] Callback hell in nodejs?

查看:211
本文介绍了回调地狱在nodejs?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中我在callbackhell?如何克服这种情况,而不使用纯javascript中的任何异步模块?

  emailCallBack(e_data,email) 
if(email_list.length){
checkEmail(email_list.pop());
} else {
completionCallback();
}

上述代码复制到多个位置,使代码按预期工作。

  function processInviteEmails(email_list,user_id,emailCallBack,completionCallback){
function checkEmail(email){
尝试{
check(email).isEmail();
//是有效的电子邮件
checkConnected(email,user_id,function(connect_status,user_row,user_meta_row,connect_row){
var e_data;
//插入连接并发送msg queue
if(connect_status ==='not connected'){
var cur_date = moment()。format('YYYY-MM-DD');
var dbData = {
first_name:'',
last_name:'',
email:email,
user_id:user_id,
status: ,
unsubscribe_token:crypto.randomBytes(6).toString('base64'),
created:cur_date,
modified:cur_date
};
ConnectModel.insert(dbData,function(result){
if(result.insertId> 0){
//发送到电子邮件队列
//队列电子邮件
MailTemplateModel .getTemplateData('invitation',function(res_data){
if(res_data.status ==='success'){
var unsubscribe_hash = crypto.createHash(md5)
.update (dbData.unsubscribe_token + email)
.digest('hex');
var unsubscribe_link = app.locals.SITE_URL +'/ unsubscribe /'+ result.insertId +'/'+ unsubscribe_hash;
var template_row = res_data.template_row;
var user_full_name = user_row.user_firstname +''+ user_row.user_lastname;
var invitation_link ='http://'+user_row.url_alias+'.'+ app.locals.SITE_DOMAIN;
var mailOptions = {
type:'invitation',
到:dbData.email,
from_name:user_full_name,
subject template_row.message_subject
.replace('[[USER]]',user_full_name),
text:template_row.message_text_body
.replace('[[USER]]',user_full_name)
.replace('[[INVITATION_LINK]]',invitation_link)
.replace('[[UNSUBSCRIBE_LINK]]',unsubscribe_link),
html:template_row.message_body
。 replace('[[USER]]',user_full_name)
.replace('[[INVITATION_LINK]]',invitation_link)
.replace('[[UNSUBSCRIBE_LINK]]',unsubscribe_link)
};
mailOptions = JSON.stringify(mailOptions);
//发送电子邮件到队列
sqsHelper.addToQueue(cfg.sqs_invitation_url,mailOptions,function(data){
if(data){
e_data = null;
}
else {
e_data = new Error('Unable to Queue');
}
emailCallBack(e_data,email);
if(email_list.length){
checkEmail(email_list.pop());
} else {
completionCallback();
}
});
}
else {
e_data = new Error('Unable to get email template');
emailCallBack(e_data,email);
if(email_list.length){
checkEmail(email_list.pop());
} else {
completionCallback();
}
}
});
}
else {
e_data = new Error('Unable to Insert connect');
emailCallBack(e_data,email);
if(email_list.length){
checkEmail(email_list.pop());
} else {
completionCallback();
}
}
});
}
else {
e_data = new Error('Already connected');
emailCallBack(e_data,email);
if(email_list.length){
checkEmail(email_list.pop());
} else {
completionCallback();
}
}
});
} catch(e){
//无效的电子邮件
emailCallBack(e,email);
if(email_list.length){
checkEmail(email_list.pop());
} else {
completionCallback();
}
}
}
checkEmail(email_list.pop());
}


解决方案

。假设您不想使用异步(我怀疑您可以证明非偏见)的解决方案包括:



1 更多的顶级功能。



2 调用这些函数,使你的代码遵循一个模式



而不是:

  saveDb1 //很多代码
saveDb2 //很多代码
sendEmail //很多代码

目标:

  function saveDb1 (arg1,arg2,callback){//顶级代码} 
函数saveDb2(arg1,arg2,callback){//顶级代码}
function sendEmail(arg1,arg2,callback){ // top-level code}
function businessLogic(){//使用上面的工作完成工作}

3 )使用更多的函数参数,而不是依赖于闭包



4 发出事件和代码!看看你如何嵌套代码向数据库写东西,然后构建电子邮件并将其添加到队列中?你不知道这两个不需要存在一个在另一个的顶部吗?



5 )Decouple(电子邮件)非常适合发送事件的核心业务逻辑和收听这些事件并排队邮件的电子邮件模块。应用程序级服务连接代码来自特定事务业务逻辑。



6 应该更广泛地处理与网络服务的连接,而不是嵌入一组特定的业务逻辑。示例



至于应该使用异步库,您可以自己考虑,但 AFTER 非常好,每一种方法:


  1. 回调和基本的功能JavaScript技术





  2. 辅助程式库(async,step,nimble等) b

    任何严重的node.js开发人员都知道如何在 ALL 范例中使用和工作。是的,每个人都有他们喜欢的方法,也许一些书呆子关于非优惠的方法,但没有一个是困难的,它是不好的设置在你的决定,而不能指向你从头写的一些非平凡的代码每个范例。此外,你应该尝试几个帮助库,并了解它们如何工作,以及为什么要保存你的样板。研究Tim Caswell的 Step 或Caolan McMahon的 async 的工作将是非常有启发性的。你看过 everyauth 源代码使用promises吗?我不喜欢它个人,但我肯定不得不承认,作者挤在附近的每一个最后一点重复出来的图书馆,他的使用承诺的方式会把你的大脑变成一个椒盐卷饼。这些人是有很多教的巫师。



    此外,一个很好的外部资源是 callbackhell .com


    In below code am I in callbackhell? How to overcome such scenario without using any async modules in pure javascript?

    emailCallBack(e_data, email);
    if (email_list.length) {
      checkEmail(email_list.pop());
    } else {
      completionCallback();
    }
    

    The above code is copied in multiple location to make code work as expected.

    function processInviteEmails(email_list, user_id, emailCallBack, completionCallback){
          function checkEmail(email){
            try {
              check(email).isEmail();
              //is valid email
              checkConnected(email, user_id, function(connect_status, user_row, user_meta_row, connect_row){
                var e_data;
                //insert to connect and send msg to queue
                if(connect_status === 'not connected'){
                  var cur_date = moment().format('YYYY-MM-DD');
                  var dbData = {
                    "first_name": '',
                    "last_name": '',
                    "email": email,
                    "user_id": user_id,
                    "status": "invited",
                    "unsubscribe_token": crypto.randomBytes(6).toString('base64'),
                    "created": cur_date,
                    "modified": cur_date
                  };
                  ConnectModel.insert(dbData, function(result){
                    if (result.insertId > 0) {
                      //send to email queue
                      //Queue Email
                      MailTemplateModel.getTemplateData('invitation', function(res_data){
                        if(res_data.status === 'success'){
                          var unsubscribe_hash = crypto.createHash("md5")
                            .update(dbData.unsubscribe_token + email)
                            .digest('hex');
                          var unsubscribe_link = app.locals.SITE_URL+'/unsubscribe/' + result.insertId + '/' + unsubscribe_hash;
                          var template_row = res_data.template_row;
                          var user_full_name = user_row.user_firstname+' '+ user_row.user_lastname;
                          var invitation_link = 'http://'+user_row.url_alias+'.'+ app.locals.SITE_DOMAIN;
                          var mailOptions = {
                            "type": 'invitation',
                            "to": dbData.email,
                            "from_name" : user_full_name,
                            "subject": template_row.message_subject
                              .replace('[[USER]]',  user_full_name),
                            "text": template_row.message_text_body
                              .replace('[[USER]]', user_full_name)
                              .replace('[[INVITATION_LINK]]', invitation_link)
                              .replace('[[UNSUBSCRIBE_LINK]]', unsubscribe_link),
                            "html": template_row.message_body
                              .replace('[[USER]]', user_full_name)
                              .replace('[[INVITATION_LINK]]', invitation_link)
                              .replace('[[UNSUBSCRIBE_LINK]]', unsubscribe_link)
                          };
                          mailOptions = JSON.stringify(mailOptions);
                          //send email to queue
                          sqsHelper.addToQueue(cfg.sqs_invitation_url, mailOptions, function(data){
                            if(data){
                              e_data = null;
                            }
                            else{
                              e_data = new Error('Unable to Queue ');
                            }
                            emailCallBack(e_data, email);
                            if (email_list.length) {
                              checkEmail(email_list.pop());
                            } else {
                              completionCallback();
                            }
                          });
                        }
                        else{
                          e_data = new Error('Unable to get email template');
                          emailCallBack(e_data, email);
                          if (email_list.length) {
                            checkEmail(email_list.pop());
                          } else {
                            completionCallback();
                          }
                        }
                      });
                    }
                    else{
                      e_data = new Error('Unable to Insert connect');
                      emailCallBack(e_data, email);
                      if (email_list.length) {
                        checkEmail(email_list.pop());
                      } else {
                        completionCallback();
                      }
                    }
                  });
                }
                else{
                  e_data = new Error('Already connected');
                  emailCallBack(e_data, email);
                  if (email_list.length) {
                    checkEmail(email_list.pop());
                  } else {
                    completionCallback();
                  }
                }
              });
            } catch (e) {
              //invalid email
              emailCallBack(e, email);
              if (email_list.length) {
                checkEmail(email_list.pop());
              } else {
                completionCallback();
              }
            }
          }
          checkEmail(email_list.pop());
        }
    

    解决方案

    Yes you are in callback hell. The solution assuming you don't want to use async (which I doubt you can justify other than prejudice) consists of:

    1) Make more top-level functions. Each function should perform either 1 or 2 IO operations as a rule of thumb.

    2) Call those functions, making your code follow a pattern of a long list of short core functions organized into business logic by a small list of control flow "glue" functions.

    Instead of:

    saveDb1 //lots of code
      saveDb2 //lots of code
        sendEmail //lots of code
    

    Aim for:

    function saveDb1(arg1, arg2, callback) {//top-level code}
    function saveDb2(arg1, arg2, callback) {//top-level code}
    function sendEmail(arg1, arg2, callback) {//top-level code}
    function businessLogic(){//uses the above to get the work done}
    

    3) Use more function arguments instead of relying so much on closures

    4) Emit events and DECOUPLE YOUR CODE! See how you have nested code writing stuff to the database and then building an email and adding it to a queue? Don't you see how those two do not need to exist one on top of the other? Emails lend themselves very well to a core business logic emitting events and an email module listening to those events and queueing the mail.

    5) Decouple application-level service connection code from specific transaction business logic. Dealing with connections to network services should be handled more broadly and not embedded with a specific set of business logic.

    6) Read other modules for examples

    As to should you use an async library, you can and should make up your own mind about that but AFTER you know, and know pretty well, each and every one of these approaches:

    1. callbacks and basic functional javascript techniques
    2. events
    3. promises
    4. Helper libraries (async, step, nimble, etc)

    Any serious node.js developer knows how to use and work within ALL of those paradigms. Yes, everyone has their favored approach and maybe some nerd rage about the non-favored approaches, but none of these are difficult and it's bad to get set in your decision without being able to point to some non-trivial code you wrote from scratch in each paradigm. Also, you should try several helper libraries and understand how they work and why they are going to save you boilerplate. Studying the work of Tim Caswell's Step or Caolan McMahon's async is going to be very enlightening. Have you seen the everyauth source code's use of promises? I don't like it personally but I surely have to admit that the author has squeezed damn near every last bit of repetition out of that library, and the way he uses promises will turn your brain into a pretzel. These people are wizards with much to teach. Don't scoff at those libraries just for hipster points or whatever.

    Also a good external resource is callbackhell.com.

    这篇关于回调地狱在nodejs?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆