Синхронный запрос в Node.js - PullRequest
94 голосов
/ 18 мая 2011

Если мне нужно вызвать 3 http API в последовательном порядке, что будет лучшей альтернативой следующему коду:

http.get({ host: 'www.example.com', path: '/api_1.php' }, function(res) { 
  res.on('data', function(d) { 

    http.get({ host: 'www.example.com', path: '/api_2.php' }, function(res) { 
      res.on('data', function(d) { 

        http.get({ host: 'www.example.com', path: '/api_3.php' }, function(res) { 
          res.on('data', function(d) { 


          });
        });
        }
      });
    });
    }
  });
});
}

Ответы [ 18 ]

68 голосов
/ 18 мая 2011

Использование отсрочек типа Futures.

var sequence = Futures.sequence();

sequence
  .then(function(next) {
     http.get({}, next);
  })
  .then(function(next, res) {
     res.on("data", next);
  })
  .then(function(next, d) {
     http.get({}, next);
  })
  .then(function(next, res) {
    ...
  })

Если вам нужно передать прицел, просто сделайте что-то вроде этого

  .then(function(next, d) {
    http.get({}, function(res) {
      next(res, d);
    });
  })
  .then(function(next, res, d) { })
    ...
  })
53 голосов
/ 21 мая 2011

Мне также нравится решение Raynos, но я предпочитаю другую библиотеку управления потоком.

https://github.com/caolan/async

В зависимости от того, нужны ли вам результаты в каждой последующей функции, я бы использовал серию, параллель или водопад.

Series , когда они должны выполняться последовательно, но вам не обязательно нужны результаты при каждом последующем вызове функции.

Parallel , если они могут выполняться параллельно, вам не нужны результаты для каждой из них во время каждой параллельной функции, и вам нужен обратный вызов, когда все завершено.

Водопад , если вы хотите изменить результаты в каждой функции и перейти к следующей

endpoints = 
 [{ host: 'www.example.com', path: '/api_1.php' },
  { host: 'www.example.com', path: '/api_2.php' },
  { host: 'www.example.com', path: '/api_3.php' }];

async.mapSeries(endpoints, http.get, function(results){
    // Array of results
});
33 голосов
/ 08 апреля 2012

Вы можете сделать это, используя мою библиотеку общих узлов :

function get(url) {
  return new (require('httpclient').HttpClient)({
    method: 'GET',
      url: url
    }).finish().body.read().decodeToString();
}

var a = get('www.example.com/api_1.php'), 
    b = get('www.example.com/api_2.php'),
    c = get('www.example.com/api_3.php');
28 голосов
/ 19 марта 2015

запрос синхронизации

Самый простой из найденных и используемых мной - запрос синхронизации , который поддерживает как узел, так и браузер!

var request = require('sync-request');
var res = request('GET', 'http://google.com');
console.log(res.body.toString('utf-8'));

Вот и все, без сумасшедшей конфигурации, без сложных установок lib, хотя у нее есть запасной вариант lib.Просто работает.Я пробовал другие примеры здесь и был озадачен, когда нужно было выполнить дополнительную настройку или установка не сработала!

Примечания:

Пример sync-request Использования не играют хорошо, когда вы используете res.getBody(), все, что делает get body, это принимает кодировку и преобразовывает данные ответа.Просто сделайте res.body.toString(encoding) вместо этого.

20 голосов
/ 18 мая 2011

Я бы использовал рекурсивную функцию со списком apis

var APIs = [ '/api_1.php', '/api_2.php', '/api_3.php' ];
var host = 'www.example.com';

function callAPIs ( host, APIs ) {
  var API = APIs.shift();
  http.get({ host: host, path: API }, function(res) { 
    var body = '';
    res.on('data', function (d) {
      body += d; 
    });
    res.on('end', function () {
      if( APIs.length ) {
        callAPIs ( host, APIs );
      }
    });
  });
}

callAPIs( host, APIs );

edit: запрос версии

var request = require('request');
var APIs = [ '/api_1.php', '/api_2.php', '/api_3.php' ];
var host = 'www.example.com';
var APIs = APIs.map(function (api) {
  return 'http://' + host + api;
});

function callAPIs ( host, APIs ) {
  var API = APIs.shift();
  request(API, function(err, res, body) { 
    if( APIs.length ) {
      callAPIs ( host, APIs );
    }
  });
}

callAPIs( host, APIs );

edit: запрос / асинхронная версия

var request = require('request');
var async = require('async');
var APIs = [ '/api_1.php', '/api_2.php', '/api_3.php' ];
var host = 'www.example.com';
var APIs = APIs.map(function (api) {
  return 'http://' + host + api;
});

async.eachSeries(function (API, cb) {
  request(API, function (err, res, body) {
    cb(err);
  });
}, function (err) {
  //called when all done, or error occurs
});
5 голосов
/ 11 сентября 2012

Другая возможность - установить обратный вызов, который отслеживает выполненные задачи:

function onApiResults(requestId, response, results) {
    requestsCompleted |= requestId;

    switch(requestId) {
        case REQUEST_API1:
            ...
            [Call API2]
            break;
        case REQUEST_API2:
            ...
            [Call API3]
            break;
        case REQUEST_API3:
            ...
            break;
    }

    if(requestId == requestsNeeded)
        response.end();
}

Затем просто назначьте каждому ID и вы можете настроить свои требования, для которых задачи должны быть выполнены до закрытия соединения.

const var REQUEST_API1 = 0x01;
const var REQUEST_API2 = 0x02;
const var REQUEST_API3 = 0x03;
const var requestsNeeded = REQUEST_API1 | REQUEST_API2 | REQUEST_API3;

Хорошо, это не красиво.Это просто еще один способ совершать последовательные звонки.К сожалению, NodeJS не обеспечивает самые основные синхронные вызовы.Но я понимаю, что такое приманка для асинхронности.

5 голосов
/ 06 апреля 2012

Похоже, решения этой проблемы не имеют конца, вот еще один:)

// do it once.
sync(fs, 'readFile')

// now use it anywhere in both sync or async ways.
var data = fs.readFile(__filename, 'utf8')

http://alexeypetrushin.github.com/synchronize

4 голосов
/ 25 ноября 2013

используйте последовательность.

последовательность установки sudo npm

или

https://github.com/AndyShin/sequenty

очень просто.

var sequenty = require('sequenty'); 

function f1(cb) // cb: callback by sequenty
{
  console.log("I'm f1");
  cb(); // please call this after finshed
}

function f2(cb)
{
  console.log("I'm f2");
  cb();
}

sequenty.run([f1, f2]);

также вы можете использовать цикл вроде этого:

var f = [];
var queries = [ "select .. blah blah", "update blah blah", ...];

for (var i = 0; i < queries.length; i++)
{
  f[i] = function(cb, funcIndex) // sequenty gives you cb and funcIndex
  {
    db.query(queries[funcIndex], function(err, info)
    {
       cb(); // must be called
    });
  }
}

sequenty.run(f); // fire!
3 голосов
/ 16 ноября 2018

Начиная с 2018 года и используя модули и обещания ES6, мы можем написать такую ​​функцию:

import { get } from 'http';

export const fetch = (url) => new Promise((resolve, reject) => {
  get(url, (res) => {
    let data = '';
    res.on('end', () => resolve(data));
    res.on('data', (buf) => data += buf.toString());
  })
    .on('error', e => reject(e));
});

и затем в другом модуле

let data;
data = await fetch('http://www.example.com/api_1.php');
// do something with data...
data = await fetch('http://www.example.com/api_2.php');
// do something with data
data = await fetch('http://www.example.com/api_3.php');
// do something with data

Код должен выполняться в асинхронном контексте (с использованием ключевого слова async)

3 голосов
/ 19 мая 2011

Использование библиотеки request может помочь минимизировать затраты:

var request = require('request')

request({ uri: 'http://api.com/1' }, function(err, response, body){
    // use body
    request({ uri: 'http://api.com/2' }, function(err, response, body){
        // use body
        request({ uri: 'http://api.com/3' }, function(err, response, body){
            // use body
        })
    })
})

Но для максимальной привлекательности вы должны попробовать некоторую библиотеку потока управления, такую ​​как Step - она ​​также позволит вам распараллеливать запросы, предполагая, что это приемлемо:

var request = require('request')
var Step    = require('step')

// request returns body as 3rd argument
// we have to move it so it works with Step :(
request.getBody = function(o, cb){
    request(o, function(err, resp, body){
        cb(err, body)
    })
}

Step(
    function getData(){
        request.getBody({ uri: 'http://api.com/?method=1' }, this.parallel())
        request.getBody({ uri: 'http://api.com/?method=2' }, this.parallel())
        request.getBody({ uri: 'http://api.com/?method=3' }, this.parallel())
    },
    function doStuff(err, r1, r2, r3){
        console.log(r1,r2,r3)
    }
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...