Nodejs Synchronous Для каждого цикла

Я хочу сделать для каждого цикла, но он запускается синхронно. Каждая итерация цикла будет выполнять вызов http.get, и он вернет json, чтобы он вставлял значения в базу данных. Проблема в том, что цикл for работает асинхронно, и это приводит к тому, что все http.gets запускаются сразу, а моя база данных не вставляет все данные. Я использую async-foreach, чтобы попытаться сделать то, что я хочу это сделать, но я не должен использовать его, если я могу сделать это правильно.

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);
};

Ответ 1

Я узнал, что я не освобождал свои mysql-соединения после того, как я закончил с каждым вызовом, и это связало подключения, из-за которых он вышел из строя и, похоже, проблема с синхронизацией.

После явного вызова connection.release(); он заставил мой код работать на 100% корректно даже в асинхронном режиме.

Спасибо за тех, кто отправил на этот вопрос.

Ответ 2

С рекурсией код довольно чистый. Подождите, пока ответ 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);

Решение, использующее обещания, также будет работать хорошо и более кратко. Например, если у вас есть версия get, которая возвращает обещание, и Node v7. 6+, вы можете написать функцию async/await, как в этом примере.

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);

Примечание: оба эти примера пропускают обработку ошибок, но, вероятно, вы должны иметь это в производственном приложении.

Ответ 3

Для циклического и синхронного связывания асинхронных действий самое чистое решение, вероятно, состоит в том, чтобы использовать библиотеку обещаний (обещания внедряются в ES6, это путь).

Используя 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
});

Ответ 4

"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);
});

Ответ 5

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
   });
}