Как сделать последовательные HTTP-вызовы? - PullRequest
2 голосов
/ 07 июня 2019

У меня есть пара API, которые мне нужно вызвать, чтобы собрать и объединить информацию.Я делаю первый вызов API и, основываясь на результате, я делаю несколько вызовов на второй (в цикле).Поскольку http-запросы асинхронны, я теряю информацию.Ко времени окончания второго шага сервер (nodejs) уже отправил ответ клиенту.

Я уже пытался каким-то образом использовать функции обратного вызова.Это позволило удержать ответ на ожидании клиента, но информация о втором вызове все еще была потеряна.Я предполагаю, что переменные не синхронизируются.Я также провел быстрое тестирование с away / async, но моего Javascript mojo было недостаточно, чтобы запустить его без ошибок.

/* pseudo code */
function getData(var1, callback){

url= "http://test.server/bla?param="+var1;

request.get(url, function (error, response, body){
var results = [];
  for(var item of JSON.parse(body).entity.resultArray) {  
     var o = {};
     o['data1'] = item.data1;
     o['data2'] = item.data2;
     o['data3'] = item.data3;

     getSecondStep(o, function(secondStepData){
       //console.log("Callback object");
       //console.log(o);
       o['secondStepData'] = secondStepData;
     });
     results.push(o);
  }
  callback(results);
});
}

function getSecondStep(object, callback){

  url = "http://othertest.server/foobar?param=" + object.data1;
  request.get(url, function (error, response, body){
    var results = [];
    if(response.statusCode == 200){
      for(var item of JSON.parse(body).object.array) {
        var o = {}
        o['data4'] = item.data4;
        o['data5'] = item.data5;
        results.push(o);
      }
      callback(results);
    }
});
}

Я хотел бы иметь возможность собирать всю информацию в один объект JSONвернуть его обратно клиенту.Клиент будет тогда ответственен за то, чтобы сделать это хорошим способом.

Ответы [ 5 ]

2 голосов
/ 07 июня 2019

Я рекомендую использовать шаблон async / await с библиотекой request-обещание-native .

Это делает вызовы API действительно простыми, и код более чистый при использовании этого шаблона.

В приведенном ниже примере я просто вызываю httpbin API для генерации UUID, но принцип применимдля любого API.

const rp = require('request-promise-native');

async function callAPIs() {
    let firstAPIResponse = await rp("https://httpbin.org/uuid", { json: true });
    console.log("First API response: ", firstAPIResponse);

    // Call several times, we can switch on the first API response if we like.
    const callCount = 3;
    let promiseList = [...Array(callCount).keys()].map(() => rp("https://httpbin.org/uuid", { json: true }));
    let secondAPIResponses = await Promise.all(promiseList);

    return { firstAPIResponse: firstAPIResponse, secondAPIResponses: secondAPIResponses };
}

async function testAPIs() {
    let combinedResponse = await callAPIs();
    console.log("Combined response: " , combinedResponse);
}

testAPIs();

В этом простом примере мы получаем комбинированный ответ примерно так:

{
    { 
        firstAPIResponse: { uuid: '640858f8-2e69-4c2b-8f2e-da8c68795f21' },
        secondAPIResponses: [ 
            { uuid: '202f9618-f646-49a2-8d30-4fe153e3c78a' },
            { uuid: '381b57db-2b7f-424a-9899-7e2f543867a8' },
            { uuid: '50facc6e-1d7c-41c6-aa0e-095915ae3070' } 
        ] 
    }
}
0 голосов
/ 07 июня 2019

Проблема в том, что вы вызываете обратный вызов, пока у вас все еще происходят асинхронные вызовы.Возможны несколько подходов, таких как использование async / await или возврат к обещаниям (что я, вероятно, сделал бы в вашем случае).

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

function getData(var1, callback){

  url= "http://test.server/bla?param="+var1;

  request.get(url, function (error, response, body){
    var results = [];
    var items = JSON.parse(body).entity.resultArray;
    var done = 0, max = items.length;
    for(var item of items) {  
       var o = {};
       o['data1'] = item.data1;
       o['data2'] = item.data2;
       o['data3'] = item.data3;

       getSecondStep(o, function(secondStepData){
         //console.log("Callback object");
         //console.log(o);
         o['secondStepData'] = secondStepData;
         results.push(o);
         done += 1;
         if(done === max) callback(results);
       });
    }
});
}

(обратите внимание, что, поскольку это псевдокод, я не проверяю ошибки и не обрабатываю возможный пустой результат из request.get(...))

0 голосов
/ 07 июня 2019

Вы должны вызывать callback первой функции только тогда, когда были вызваны все вторые callback функции.Попробуйте это изменить:

function getData(var1, callback) {

    url = "http://test.server/bla?param=" + var1;

    request.get(url, function (error, response, body) {
        var results = [],count=0;
        var arr = JSON.parse(body).entity.resultArray;
        for (let [index, value]  of arr.entries()) {
            var o = {};
            o['data1'] = item.data1;
            o['data2'] = item.data2;
            o['data3'] = item.data3;

            getSecondStep(o, function (secondStepData) {
                //console.log("Callback object");
                //console.log(o);
                o['secondStepData'] = secondStepData;

                results[index] = o;
                count++;
                if (count === arr.length) {
                    callback(results);
                }
            });

        }
    });
}
0 голосов
/ 07 июня 2019

Я предлагаю вам перейти в библиотеку, которая поддерживает обещания (например: https://github.com/request/request-promise), так как с кодом становится намного проще работать, чем с методом обратного вызова.

Ваш код будет выглядеть примерно так:

function getData(var1){
    var url = "http://test.server/bla?param="+var1;
    return request.get(url).then(result1 => {
        var arr = JSON.parse(body).entity.resultArray;
        return Promise.all( arr.map(x => request.get("http://othertest.server/foobar?param=" + result1.data1)))
                     .then(result2 => {
                              return {
                                data1: result1.data1,
                                data2: result1.data2,
                                data3: result1.data3,
                                secondStepData: result2.map(x => ({data4:x.data4, data5:x.data5}))
                             }
                      })
    });
}

И использование будет

getData("SomeVar1").then(result => ... );
0 голосов
/ 07 июня 2019

мы можем делать синхронные вызовы, сохраняя async: false в вызове jquery ajax. см. ниже может быть полезным

$.ajax({
url: url, 
data: {  },
method: 'GET',
async : false,
success: function (data) {
if (data != null) {
         //write your code here                  
    },
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...