2016-05-26 22 views
1

昨日からnode.jsのasyncモジュールを使用して、非同期タスクの順序を処理しています。以下のコードは動作しますが、エラー:Node.js非同期ウォールフォール()コールバックがすでに呼び出されていた

Callback was already called

が約1〜5回表示されます。私はどこに問題があるのか​​わからない、たぶんforEachループのgetNewsTitles()のコールバックは一度だけではない。だから、console.logをここに入れますが、エラーが表示されているかどうかにかかわらず、このログは1回だけ印刷されます。

async.waterfall([ 
    function(callback) { 
    callback(); 
    }, 
    function(callback) { 
    // When error, below log doesn't show. 
    console.log('getting news titles...'); 
    getNewsTitles(arr_uri, arr_subs, function() { 
     // * The problem is here => "Callback was already called" 
     callback(null); 
    }); 
    } 
], function(err, result) { 
    if (err) return next(); 
    else res.send(arr_subs); 
}); 

function getNewsTitles(targets, subs, callback) { 
    targets.forEach(function(current, index) { 
    request.get({ 
     uri: current, 
     encoding: null 
    }, function(err, response, body) { 
     if (!err && response.statusCode == 200) { 
     var $ = cheerio.load(iconv.decode(body, 'EUC-KR')); 
     var subject = $('.articleSubject a'); 
     for (var i = 0; i < subject.length; i++) { 
      subs.push(subject[i].attribs.title); 
     } 
     if (subs.length == (targets.length - 2) * 20 + 2) { 
      // when error or not, below log shows one time. 
      console.log('doubt here too'); 
      callback(); 
     } 
     } 
    }); 
    }) 
} 

私は何かを逃しましたか?

答えて

1

request.get()は非同期呼び出しです。通常のループは機能しません。上記のコードでは、成功したすべてのrequest.get()が呼び出されるとcallback()が呼び出されます。 async.each()、async.eachLimit()、async.eachSeries()など、フローを制御できるものが必要なので、callback()は一度だけ呼び出されます。

このシナリオでasync.each()を使用してasync.eachLimit()を使用して、request.get()の最大数を制限して、あまりにも多くのrequest.get()でサーバーをフラッシュしないことをお勧めします時間。以下の例では、最大5つの要求を同時に処理していますが、サーバーが処理できる値を変更できます。

function getNewsTitles(targets, subs, callback) { 
    async.eachLimit(targets, 5, function (current, eachCb) { 
     request.get({ 
      uri: current, 
      encoding: null 
     }, function(err, response, body) { 
      if (!err && response.statusCode == 200) { 
       var $ = cheerio.load(iconv.decode(body, 'EUC-KR')); 
       var subject = $('.articleSubject a'); 
       for (var i = 0; i < subject.length; i++) { 
        subs.push(subject[i].attribs.title); 
       } 
       if (subs.length == (targets.length - 2) * 20 + 2) { 
        // when error or not, below log shows one time. 
        console.log('doubt here too'); 
       } 
      } 
      eachCb(null); // must be called for every iteration of async.eachLimit() 
     }); 
    }, function (err) { 
     callback(null); // all items have been processed, call this callback only once 
    }); 
} 
+0

ありがとう^ - ^それはうまくいく。しかし、私はあなたが** callback()が成功したすべてのrequest.get()を呼び出すと言ったのだろうか。**。しかし、私はこのif(subs.length ==(targets.length - 2)* 20 + 2){callback()によって一度だけ呼び出されるコールバック()を意図していました。 } 'この条件はforループで一度だけ一致します。あなたのコードはうまく動作し、とても感謝していますが、私のコードがエラーを表示した理由を理解できません...新しい質問をする方が良いですか?ありがとう。 – Juntae

+0

私はfiddle https://jsfiddle.net/bexoss/1xqLhqrr/を書きました。私はこれが質問のコードと同じだと思いますが、問題はうまくいきませんが、フィドルコードはうまくいきます。...同じ問題が起こったら、新しい質問をする必要があるかもしれません。 – Juntae

+0

実際、あなたは正しいです...すべての成功したリクエストではありません.get()...私は1番目(エラーをチェックする)を見ましたが、2番目の場合はそれを持っていれば近くに見ていませんでした。 – Ben

関連する問題