danielnewman
@danielnewman
Front-end

Как в NodeJS сделать EventEmitter для нескольких одновременных запросов?

Запрашиваю два API, возвращающих данные источника и хранилища данных. Заполняю ими БД моего приложения, сравнивая данные источника и хранимые, для последующего обновления:

var dataStoredLoad = function( dataListOffset ) {
    eventEmitter.emit('dataStoredFetched');
}
var dataSourceLoad = function( dataListOffset ) {
    eventEmitter.emit('dataSourceFetched');
}

var middlewareDataToUpdate = function ( dataStoredLoad , dataSourceLoad ) {
    dataStoredLoad.forEach( function (dataStored, index) {
        dataSourceLoad.forEach( function (dataStored, index) {
            if ( dataStored.id == dataSource ) {
                 // key.value comparsion procedure and .pop() / .push()
            }
        }
    }
}


Оба API работаю async, а сравнение нужно запускать лишь когда оба массива данных загружены в БД.
Неизвестно, какой API вернет значения первым.

Что тут можно сделать и как это красиво пишут другие?

UPD:
Внутри функций - запросы к API, выполненные в неблокирующем async потоке, а забор данных выполняется в серии обращений через API, т.е. решение с async.parallel, предложенное также ниже, всегда валится в "колбэк(?)" function(err, results) где results is equal to: { one: ' ', two: ' ' }

Видимо, я как-то должен вопрос переформулировать, но не понимаю, как именно.

UPD2:

var answerAPI = crunchbase.request(arg1) ;

не null, с момента запуска, а сразу какая-то '', в ожидании ответа сервера API. что запрокидывает нас дальше по этому водопаду асинхронных функций к функции результата, функция отрабатывается с пустыми значениями, а потом (в моем случае) в консоль валятся результаты запросов к API.

async.series({
  one: function(callback) {
      var answerAPIone = crunchbase.request(arg1);
      callback(null, answerAPIone );
  },
  two: function(callback) {
      var answerAPItwo = airtable.request(arg1);
      callback(null, answerAPItwo);
  }
},
function(err, results) {
  console.log( 'ИТОГО: <', results.one, '><', results.two, '>' );
  // ИТОГО: <><>
});


Все уперлось в правильное написание функции с колбеком и использование async по его прямому назначению. Остальные варианты - замечательно расширили горизонты. Всем спасибо.
  • Вопрос задан
  • 3481 просмотр
Решения вопроса 1
MarcusAurelius
@MarcusAurelius Куратор тега Node.js
автор Impress Application Server для Node.js
https://github.com/caolan/async
async.series({
  one: function(callback) {
    setTimeout(function() {
      callback(null, 1);
    }, 200);
  },
  two: function(callback) {
    setTimeout(function() {
      callback(null, 2);
    }, 100);
  }
},
function(err, results) {
  // results is now equal to: { one: 1, two: 2 }
});
Ответ написан
Пригласить эксперта
Ответы на вопрос 4
Я советую вам посмотреть в сторону обещаний.
Начните с канонической библиотека promise
Когда разберетесь с ней, вам возможно потребуется комбайн посложнее, смотрите в сторону Q

// npm install promise

var fs = require('fs');
var Promise = require('promise');

// В качестве источника данных в данном случае файловая система,
// в вашем случае это видимо будет net.Connection
function dataStoredLoad( dataName ) {
    return new Promise(function(resolve, reject) {
        fs.readFile(dataName, 'utf8', function(err, data) {
            if (err) return reject(err);

            resolve(data);
        });
    })
}

// Это другой способ записать то, что написано в dataStoredLoad
function dataSourceLoad( dataName ) {
    return Promise.denodeify(fs.readFile)(dataName, 'utf8');
}

Promise.all(dataStoredLoad, dataSourceLoad).then(function(res) {
    var stored = res[0],
          sourse = res[1];

    stored.forEach( function (dataStored, index) {
        sourse.forEach( function (dataStored, index) {
            if ( dataStored.id == dataSource ) {
                 // key.value comparsion procedure and .pop() / .push()
            }
        }
    }
});
Ответ написан
@dtestyk
однажды для подобной проблемы коллега предложил считать события.

также, можно использовать Promise.all(db_promise, src_promise), при некотором старании и Promise.race даже получится обработать таймаут.
Ответ написан
Комментировать
Во-первых, в случае async вам больше подходит parallel, а не series. async.parallel не ждет вызова колбека из первой функции и сразу запустит вторую после возврата первой.
Во-вторых, API, которые вы запрашиваете, должны быть (и наверняка являются) асинхронными, а потому сразу возвращают вам ничего. Вы должны передавать этим API колбек, внутри которого вызывать колбек асинка.

Как-то так:
async.parallel({
  one: function(callback) {
      crunchbase.request(arg1, function(error, answerAPIone){
        callback(null, answerAPIone);
      });
  },
  two: function(callback) {
      airtable.request(arg1, function(error, answerAPItwo){
        callback(null, answerAPItwo);
      });
  }
},
function(err, results) {
  console.log( 'ИТОГО: <', results.one, '><', results.two, '>' );
});
Ответ написан
Комментировать
isqua
@isqua
Научу HTML, CSS, JS, BEM и Git
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы