Синхронный запрос в 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 ]

2 голосов
/ 21 мая 2011

Существует множество библиотек потоков управления - мне нравится conseq (... потому что я написал это). Кроме того, on('data') может запускаться несколько раз, поэтому используйте библиотеку оболочки REST, такую ​​как * 1004. * restler .

Seq()
  .seq(function () {
    rest.get('http://www.example.com/api_1.php').on('complete', this.next);
  })
  .seq(function (d1) {
    this.d1 = d1;
    rest.get('http://www.example.com/api_2.php').on('complete', this.next);
  })
  .seq(function (d2) {
    this.d2 = d2;
    rest.get('http://www.example.com/api_3.php').on('complete', this.next);
  })
  .seq(function (d3) {
    // use this.d1, this.d2, d3
  })
2 голосов
/ 12 апреля 2017

На этот вопрос хорошо ответил Райнос. Однако с тех пор, как ответ был опубликован, в библиотеке последовательностей произошли изменения.

Чтобы последовательность работала, перейдите по этой ссылке: https://github.com/FuturesJS/sequence/tree/9daf0000289954b85c0925119821752fbfb3521e.

Вот как вы можете заставить его работать после npm install sequence:

var seq = require('sequence').Sequence;
var sequence = seq.create();

seq.then(function call 1).then(function call 2);
1 голос
/ 28 января 2016

Я попал сюда, потому что мне нужно было ограничить скорость http.request (~ 10 000 запросов агрегации для упругого поиска для создания аналитического отчета).Следующие слова просто заглушили мою машину.

for (item in set) {
    http.request(... + item + ...);
}

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

Моя работа не зависела от порядка, и мой первый подход к решению этой проблемы заключался в том, чтобы обернуть его в сценарий оболочки, чтобы разделить его на части (потому что я новичокв JavaScript).Это было функционально, но не удовлетворительно.В конце концов, моим JavaScript-решением было сделать следующее:

var stack=[];
stack.push('BOTTOM');

function get_top() {
  var top = stack.pop();
  if (top != 'BOTTOM')
    collect(top);
}

function collect(item) {
    http.request( ... + item + ...
    result.on('end', function() {
      ...
      get_top();
    });
    );
}

for (item in set) {
   stack.push(item);
}

get_top();

Это похоже на взаимную рекурсию между collect и get_top .Я не уверен, что это действительно так, потому что система асинхронная, а функция collect завершается с обратным вызовом, сохраненным для события в on. ('End' .

Я думаю, что это достаточно общий подход, чтобы применить его к исходному вопросу. Если, как и в моем сценарии, последовательность / набор известны, все URL / ключи могут быть помещены в стек за один шаг. Если они вычисляются по ходу, on ('end' функция может выдвинуть следующий URL в стеке непосредственно перед get_top () . В любом случае, результат имеет меньшую вложенность и может быть легче реорганизовать, когда APIвы вызываете изменения.

Я понимаю, что это фактически эквивалентно простой рекурсивной версии @ generalhenry выше (так что я проголосовал за это!)

1 голос
/ 17 декабря 2015

... 4 года спустя ...

Вот оригинальное решение с каркасом Danf (вам не нужен код для этого видавещей, только некоторые настройки):

// config/common/config/sequences.js

'use strict';

module.exports = {
    executeMySyncQueries: {
        operations: [
            {
                order: 0,
                service: 'danf:http.router',
                method: 'follow',
                arguments: [
                    'www.example.com/api_1.php',
                    'GET'
                ],
                scope: 'response1'
            },
            {
                order: 1,
                service: 'danf:http.router',
                method: 'follow',
                arguments: [
                    'www.example.com/api_2.php',
                    'GET'
                ],
                scope: 'response2'
            },
            {
                order: 2,
                service: 'danf:http.router',
                method: 'follow',
                arguments: [
                    'www.example.com/api_3.php',
                    'GET'
                ],
                scope: 'response3'
            }
        ]
    }
};

Используйте то же значение order для операций, которые вы хотите выполнять параллельно.

Если вы хотитекороче говоря, вы можете использовать процесс сбора:

// config/common/config/sequences.js

'use strict';

module.exports = {
    executeMySyncQueries: {
        operations: [
            {
                service: 'danf:http.router',
                method: 'follow',
                // Process the operation on each item
                // of the following collection.
                collection: {
                    // Define the input collection.
                    input: [
                        'www.example.com/api_1.php',
                        'www.example.com/api_2.php',
                        'www.example.com/api_3.php'
                    ],
                    // Define the async method used.
                    // You can specify any collection method
                    // of the async lib.
                    // '--' is a shorcut for 'forEachOfSeries'
                    // which is an execution in series.
                    method: '--'
                },
                arguments: [
                    // Resolve reference '@@.@@' in the context
                    // of the input item.
                    '@@.@@',
                    'GET'
                ],
                // Set the responses in the property 'responses'
                // of the stream.
                scope: 'responses'
            }
        ]
    }
};

Посмотрите на обзор структуры для получения дополнительной информации.

1 голос
/ 03 ноября 2014

Вот моя версия @ andy-shin с аргументами в массиве вместо индекса:

function run(funcs, args) {
    var i = 0;
    var recursive = function() {
        funcs[i](function() {
            i++;
            if (i < funcs.length)
                recursive();
        }, args[i]);
    };
    recursive();
}
0 голосов
/ 04 августа 2015

Супер запрос

Это еще один синхронный модуль, основанный на запросах и использующий обещания. Супер простой в использовании, хорошо работает с тестами мокко.

npm install super-request

request("http://domain.com")
    .post("/login")
    .form({username: "username", password: "password"})
    .expect(200)
    .expect({loggedIn: true})
    .end() //this request is done 
    //now start a new one in the same session 
    .get("/some/protected/route")
    .expect(200, {hello: "world"})
    .end(function(err){
        if(err){
            throw err;
        }
    });
0 голосов
/ 13 марта 2019

Я действительно получил именно то, что вы (и я) хотели, без использования await, Promises или включений какой-либо (внешней) библиотеки (кроме нашей).

Вот как это сделать:

Мы собираемся сделать модуль C ++ для работы с node.js, и эта функция модуля C ++ сделает HTTP-запрос и вернет данные в виде строки, и вы можете использовать это непосредственно, выполнив:

var myData = newModule.get(url);

ВЫ ГОТОВЫ начать?

Шаг 1: создайте новую папку где-нибудь еще на вашем компьютере, мы используем эту папку только для создания файла module.node (скомпилированного из C ++), вы можете переместить его позже.

В новой папке (я поместил мою в mynewFolder / src для организации):

npm init

затем

npm install node-gyp -g

теперь создайте 2 новых файла: 1, вызвал что-то .cpp и для вставки этого кода в него (или измените его, если хотите):

#pragma comment(lib, "urlmon.lib")
#include <sstream>
#include <WTypes.h>  
#include <node.h>
#include <urlmon.h> 
#include <iostream>
using namespace std;
using namespace v8;

Local<Value> S(const char* inp, Isolate* is) {
    return String::NewFromUtf8(
        is,
        inp,
        NewStringType::kNormal
    ).ToLocalChecked();
}

Local<Value> N(double inp, Isolate* is) {
    return Number::New(
        is,
        inp
    );
}

const char* stdStr(Local<Value> str, Isolate* is) {
    String::Utf8Value val(is, str);
    return *val;
}

double num(Local<Value> inp) {
    return inp.As<Number>()->Value();
}

Local<Value> str(Local<Value> inp) {
    return inp.As<String>();
}

Local<Value> get(const char* url, Isolate* is) {
    IStream* stream;
    HRESULT res = URLOpenBlockingStream(0, url, &stream, 0, 0);

    char buffer[100];
    unsigned long bytesReadSoFar;
    stringstream ss;
    stream->Read(buffer, 100, &bytesReadSoFar);
    while(bytesReadSoFar > 0U) {
        ss.write(buffer, (long long) bytesReadSoFar);
        stream->Read(buffer, 100, &bytesReadSoFar);
    }
    stream->Release();
    const string tmp = ss.str();
    const char* cstr = tmp.c_str();
    return S(cstr, is);
}

void Hello(const FunctionCallbackInfo<Value>& arguments) {
    cout << "Yo there!!" << endl;

    Isolate* is = arguments.GetIsolate();
    Local<Context> ctx = is->GetCurrentContext();

    const char* url = stdStr(arguments[0], is);
    Local<Value> pg = get(url,is);

    Local<Object> obj = Object::New(is);
    obj->Set(ctx,
        S("result",is),
        pg
    );
    arguments.GetReturnValue().Set(
       obj
    );

}

void Init(Local<Object> exports) {
    NODE_SET_METHOD(exports, "get", Hello);
}

NODE_MODULE(cobypp, Init);

Теперь создайте новый файл в том же каталоге с именем something.gyp и поместите (что-то вроде) в него:

{
   "targets": [
       {
           "target_name": "cobypp",
           "sources": [ "src/cobypp.cpp" ]
       }
   ]
}

Теперь в файле package.json добавьте: "gypfile": true,

Сейчас: в консоли node-gyp rebuild

Если он проходит через всю команду и в конце скажет "ок" без ошибок, вы (почти) можете пойти, если нет, то оставьте комментарий ..

Но если это сработает, перейдите в build / Release / cobypp.node (или как его там звали), скопируйте его в папку main node.js, затем в node.js:

var myCPP = require("./cobypp")
var myData = myCPP.get("http://google.com").result;
console.log(myData);

..

response.end(myData);//or whatever
0 голосов
/ 01 октября 2018

Этот код может использоваться для синхронного и последовательного выполнения массива обещаний, после чего вы можете выполнить свой окончательный код в вызове .then().

const allTasks = [() => promise1, () => promise2, () => promise3];

function executePromisesSync(tasks) {
  return tasks.reduce((task, nextTask) => task.then(nextTask), Promise.resolve());
}

executePromisesSync(allTasks).then(
  result => console.log(result),
  error => console.error(error)
);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...