如何进行顺序HTTP调用?

问题描述 投票:0回答:5

我需要调用几个 API 来收集和合并信息。 我进行第一个 API 调用,然后根据结果对第二个 API 进行多次调用(循环中)。 由于 http 请求是异步的,我丢失了信息。当第二步完成时,服务器(nodejs)已经将响应发送回客户端。

我已经尝试过以某种方式使用回调函数。这设法使对客户端的响应保持等待,但第二次调用的信息仍然丢失。我想不知何故变量没有同步。 我还使用away/async进行了快速测试,但我的Javascript魔力不足以使其运行没有错误。

/* 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 对象中,然后将其返回给客户端。 然后客户将负责以良好的方式渲染它。

javascript node.js httprequest
5个回答
2
投票

我建议将异步/等待模式与 request-promise-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
投票

我建议您使用支持 Promise 的库(例如: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
投票

问题是您在调用回调时仍然有异步调用正在进行。有几种方法是可能的,例如我们使用 async/await,或恢复到 Promises(在您的情况下我可能会这样做)。

或者,您也可以仅在掌握了所有可用信息后才调用回调。伪代码如下:

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
投票

只有当所有第二个

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
投票

2023 年,我编写了 sync-request-curl,它有助于使用 libcurl 的 Easy 接口发送同步 HTTP 请求。

该库包含原始 sync-request 中功能的子集,但利用 node-libcurl 以获得更好的性能和 NodeJS 的附加 libcurl 选项。

这是 GET 请求的简单用法:

// import request from 'sync-request-curl';
const request = require('sync-request-curl');
const response = request('GET', 'https://ipinfo.io/json');
console.log('Status Code:', response.statusCode);
console.log('body:', response.body.toString());
console.log('json:', JSON.parse(response.body.toString()));

这是一个更接近您的用例的示例:

const request = require('sync-request-curl');

const getSecondStep = (data1) => {
  const url = 'http://othertest.server/foobar';
  const response = request('GET', url, { qs: { param: data1 }});

  if (response.statusCode === 200) {
    const body = response.body.toString('utf-8');
    const jsonBody = JSON.parse(body);
    return jsonBody.object.array.map(item => ({
      data4: item.data4,
      data5: item.data5,
    }));
  }
  return [];
};

function getData(var1) {
  const url = 'http://test.server/bla';
  const response = request('GET', url, { qs: { param: var1 }});
  const body = response.body.toString('utf-8');

  const results = [];
  const jsonBody = JSON.parse(body);
  for (const item of jsonBody.entity.resultArray) {
    results.push({
      data1: item.data1,
      data2: item.data2,
      data3: item.data3,
      secondStepData: getSecondStep(item.data1),
    });
  }
  return results;
}

const var1 = 'your_param_value';
const results = getData(var1);
console.log(results);

希望这有帮助:)。

© www.soinside.com 2019 - 2024. All rights reserved.