web-dev-qa-db-ja.com

Nodejs同期For各ループ

Forループを実行したいが、同期的に実行するようにします。ループの各反復はhttp.get呼び出しを実行し、値をデータベースに挿入するためのjsonを返します。問題は、forループが非同期で実行されるため、すべてのhttp.getsが一度にすべて実行され、データベースですべてのデータが挿入されないということです。それを行うには、しかし、私はそれを正しい方法で行うことができれば、私はそれを使用する必要はありません。

mCardImport = require('m_cardImport.js');
var http = require('http');
app.get('/path/hi', function(req, res) {

mCardImport.getList(function(sets) {
  forEach(sets, function(item, index, arr) {
    theUrl = 'http://' + sets.set_code + '.json';
    http.get(theUrl, function(res) {

      var jsonData = '';
      res.on('data', function(chunk) {
        jsonData += chunk;
      });

      res.on('end', function() {
        var theResponse = JSON.parse(jsonData);
        mCardImport.importResponse(theResponse.list, theResponse.code, function(theSet) {
          console.log("SET: " + theSet);
        });
      });
    });
  });
});
});

そして私のモデル

exports.importResponse = function(cardList, setCode, callback) {

mysqlLib.getConnection(function(err, connection) {

forEach(cardList, function(item, index, arr) {

  var theSql = "INSERT INTO table (name, code, multid, collector_set_num) VALUES "
   + "(?, ?, ?, ?) ON DUPLICATE KEY UPDATE id=id";
  connection.query(theSql, [item.name, setCode, item.multid, item.number], function(err, results) {
    if (err) {
      console.log(err);
    };
  });
});
});
callback(setCode);
};
15
user3447415

各呼び出しが終了した後、mysql接続を解放していないことがわかりました。これにより、接続が拘束されて失敗し、同期の問題のように見えます。

connection.release();を明示的に呼び出した後、非同期方式でもコードが100%正しく動作するようになりました。

この質問に投稿してくれた人に感謝します。

2
user3447415

再帰を使用すると、コードはかなりきれいになります。 http応答が戻るのを待ってから、次の試行を開始します。これは、ノードのすべてのバージョンで機能します。

var urls = ['http://stackoverflow.com/', 'http://security.stackexchange.com/', 'http://unix.stackexchange.com/'];

var processItems = function(x){
  if( x < urls.length ) {
    http.get(urls[x], function(res) {

      // add some code here to process the response

      processItems(x+1);
    });
  }
};

processItems(0);

Promiseを使用したソリューションもうまく機能し、より簡潔になります。たとえば、 promiseを返すgetのバージョン およびNode v7.6 +がある場合、この例のようなasync/await関数を記述できます。いくつかの新しいJS機能を使用します。

const urls = ['http://stackoverflow.com/', 'http://security.stackexchange.com/', 'http://unix.stackexchange.com/'];

async function processItems(urls){
  for(const url of urls) {
    const response = await promisifiedHttpGet(url);    
    // add some code here to process the response.
  }
};

processItems(urls);

注:これらの例はどちらもエラー処理をスキップしますが、実稼働アプリではおそらくそれが必要です。

49
AlexMA

非同期アクションをループして同期的にチェーンするための最もクリーンなソリューションは、Promiseライブラリを使用することです(ES6でPromiseが導入されています。これが方法です)。

Bluebird を使用すると、これは

Var p = Promise.resolve();
forEach(sets, function(item, index, arr) {
    p.then(new Promise(function(resolve, reject) {
         http.get(theUrl, function(res) {
         ....
         res.on('end', function() {
              ...
              resolve();
         }
    }));
});
p.then(function(){
   // all tasks launched in the loop are finished
});
5
Denys Séguret
"use strict";

var Promise = require("bluebird");
var some = require('promise-sequence/lib/some');

var pinger = function(wht) {
    return new Promise(function(resolve, reject) {
        setTimeout(function () { 

            console.log('I`ll Be Waiting: ' + wht);
            resolve(wht);

        }, Math.random() * (2000 - 1500) + 1500);
    });
}

var result = [];
for (var i = 0; i <= 12; i++) {
    result.Push(i);
}

some(result, pinger).then(function(result){
  console.log(result);
});
0
user956584
var urls = ['http://stackoverflow.com/', 'http://security.stackexchange.com/', 'http://unix.stackexchange.com/'];

for (i = 0; i < urls.length; i++){
   http.get(urls[i], function(res) {
       // add some code here to process the response
   });
}
0
RAHUL.S