12

以下のコードでは、私はコールバック地獄にいますか? 純粋な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){
        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());
    }
4

3 に答える 3

31

はい、あなたはコールバック地獄にいます。async を使用したくないと仮定した場合の解決策 (偏見以外に正当化できるとは思えません) は、次のもので構成されます。

1 ) より多くのトップレベル関数を作成します。経験則として、各関数は 1 つまたは 2 つの IO 操作を実行する必要があります。

2 ) これらの関数を呼び出して、制御フローの「接着」関数の小さなリストによってビジネス ロジックに編成された短いコア関数の長いリストのパターンにコードを従わせます。

それ以外の:

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

目指す:

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 ) クロージャに頼る代わりに、より多くの関数引数を使用する

4 ) イベントを発行し、コードをデカップリングします! 入れ子になったコードをデータベースに書き込み、メールを作成してキューに追加する方法がわかりますか? この 2 つが重なって存在する必要がないことがわかりませんか? 電子メールは、イベントを発行するコア ビジネス ロジックと、それらのイベントをリッスンしてメールをキューに入れる電子メール モジュールに非常に適しています。

5 ) アプリケーションレベルのサービス接続コードを特定のトランザクション ビジネス ロジックから分離します。ネットワーク サービスへの接続の処理は、特定のビジネス ロジック セットに組み込まれるのではなく、より広範に処理する必要があります。

6 ) 例として他のモジュールを読む

非同期ライブラリを使用する必要がある場合は、それについて自分で決めることができますし、そうすべきです

  1. コールバックと基本的な関数型 JavaScript テクニック
  2. イベント
  3. 約束
  4. ヘルパー ライブラリ (async、step、nimble など)

本格的な node.js 開発者は、これらすべてのパラダイムを使用して作業する方法を知っています。はい、誰もが好みのアプローチを持っており、おそらく好きではないアプローチについてのオタクの怒りがありますが、これらのどれも難しいものではなく、最初から書いた重要なコードを指摘することができずに決定を下すのは悪いことです.それぞれのパラダイム。また、いくつかのヘルパー ライブラリを試して、それらがどのように機能し、定型文を節約できるのかを理解する必要があります。ティム・キャズウェルStepやカオラン・マクマホンの作品を研究するasyncことは、非常に啓発的なものになるでしょう。見たことがありますかeveryauthソースコードの約束の使用? 私は個人的には好きではありませんが、著者がそのライブラリから最後の繰り返しのほぼすべてを搾り取ったことを認めなければなりません。彼の約束の使い方は、あなたの脳をプレッツェルに変えます. これらの人々は、教えることがたくさんある魔法使いです。流行に敏感なポイントなどのために、これらのライブラリを嘲笑しないでください。

また、適切な外部リソースはcallbackhell.comです。

于 2013-08-07T05:31:52.420 に答える