0

私はまだ非同期プログラミングに少し慣れていないので、私がやろうとしていることを達成するためのより良い方法があるかどうか疑問に思っていました.

exports.content = function(req, res){
  OPID = req.params.id;

  titles.findOne({postID: OPID}, function (err, post) { //function #1
    if (err) throw(err); 

    readComment(OPID, function(comment){ //function #2

      branchFilter.getBranches(function(branches){ //function #3

        res.render('content', {title: post.title, content: post.body, OPID: post.postID, comments: comment, branches: branches});
      })
    });
  });
};

この例では、コールバックを使用して他のモジュールからデータを取得する 3 つのネストされた関数があります。このすべてのデータを res.render ステートメントに含める必要があります。このアプローチを続けると、ネストされた関数がさらに必要になると思います。これを行うより良い方法はありますか?

4

2 に答える 2

2

たとえば、データを取得するだけの関数があります

exports.content = function(req, res){
   getData(req, function(err, data) { 
   // trivial error handling
   if (err) {
      console.dir('error getting your data', err);
      return res.redirect('/');
   }
   res.render('content', data)
}

// cb is a callback function
function getData(req, cb) {
  OPID = req.params.id;
  titles.findOne({postID: OPID}, function (err, post) { //function #1
    if (err) { return cb(err); }
    readComment(OPID, function(err, comment){ //function #2 (with error handling)
      if (err) { return cb(err); }
      branchFilter.getBranches(function(err, branches){ //function #3
        if (err) { return cb(err); }
        var output = {
          title: post.title, 
          content: post.body, 
          OPID: post.postID, 
          comments: comment, branches: branches
        }
        return cb(null, output);
      });
    });
  });
}

http://callbackhell.com/をご覧ください。コールバックを使用してクリーンなコードを作成する方法の概要がよくわかります。以下、同サイトより転載

関数に名前を付ける

以下は、browser-request を使用してサーバーへの AJAX リクエストを行う (乱雑な) ブラウザー JavaScript です。

var form = document.querySelector('form')
form.onsubmit = function(submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, function(err, response, body) {
    var statusMessage = document.querySelector('.status')
    if (err) return statusMessage.value = err
    statusMessage.value = body
  })
}

このコードには 2 つの無名関数があります。名前をつけよう!

var form = document.querySelector('form')
form.onsubmit = function formSubmit(submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, function postResponse(err, response, body) {
    var statusMessage = document.querySelector('.status')
    if (err) return statusMessage.value = err
    statusMessage.value = body
  })
}

ご覧のとおり、関数の名前付けは非常に簡単で、コードにいくつかの優れた点をもたらします。

  • コードを読みやすくする
  • 例外が発生すると、「匿名」ではなく実際の関数名を参照するスタックトレースが取得されます
  • コードを浅く保つか、深くネストしないようにすることができます。これにより、次のポイントに進みます。

コードを浅くする

最後の例に基づいて、さらに進んで、コード内で行われているトリプル レベルのネストを取り除きましょう。

function formSubmit(submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, postResponse)
}

function postResponse(err, response, body) {
  var statusMessage = document.querySelector('.status')
  if (err) return statusMessage.value = err
  statusMessage.value = body
}

document.querySelector('form').onsubmit = formSubmit

このようなコードは、見た目が怖くなく、後で編集、リファクタリング、ハッキングするのが簡単です。

モジュール化!

これが最も重要な部分です: 誰でもモジュール (別名ライブラリー) を作成できます。(node.js プロジェクトの) Isaac Schlueter の言葉を引用すると、「それぞれが 1 つのことを行う小さなモジュールを作成し、それらをより大きなことを行う別のモジュールにアセンブルします。そこに行かなければ、コールバック地獄に入ることができません。 ."

上記のボイラープレート コードを取り出し、いくつかのファイルに分割してモジュールに変換しましょう。私はブラウザとサーバーの両方で JavaScript を書いているので、両方で動作するメソッドを示しますが、それでも素晴らしくシンプルです。

以前の 2 つの関数を含む formuploader.js という名前の新しいファイルを次に示します。

function formSubmit(submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, postResponse)
}

function postResponse(err, response, body) {
  var statusMessage = document.querySelector('.status')
  if (err) return statusMessage.value = err
  statusMessage.value = body
}

exports.submit = formSubmit
于 2013-02-11T03:46:55.783 に答える
1

JohnnyHK が示唆したように、優れた非同期ライブラリがこれに役立ちます。Caolan の asyncもお勧めします。autoとを組み合わせることで、ほとんどすべての非同期問題を回避できforEachますが、ランタイムが問題でasyncある場合は最適な選択ではない可能性があります。私はあなたのコードを次のように書き直します:

exports.content = function(req, res){
  OPID = req.params.id;

  async.auto({
    post: function(next) {
      titles.findOne({postID: OPID}, next);
    },
    comment: function(next) {
      readComment(OPID, function(comment){ next(null, comment); });
    },
    branches: function(next) {
      branchFilter.getBranches(function(branches){ next(null, branches); });
    }
  }, function(err, results) {
    if(err) throw(err);
    res.render('content', {title: results.post.title, content: results.post.body, OPID: results.post.postID, comments: results.comment, branches: results.branches});
  });
};

auto関数のディクショナリ (ディクショナリ内の他の関数へのオプションの依存関係があります。詳細については、非同期の readme を参照してください) を取り、結果をディクショナリとして 2 番目の引数である "final" 関数に渡します。

いずれかの関数がそのコールバック (最初の引数) に null 以外を渡すと、err他の関数の呼び出しを停止し、すぐに結果を「最終」関数に渡します。errnull

branchesand関数で無名関数を渡さなければならなかったことに注意してくださいcomment。これはauto、このライブラリの他の関数と同様にasync、コールバックの最初の引数がエラー値で、2 番目の引数が結果であると想定しているためです。

于 2013-02-10T18:03:37.683 に答える