使用nodejs如何在循环中返回包含一批API调用中所有数据的对象?

问题描述 投票:1回答:2

我知道JavaScript中的闭包和回调,但显而易见的是我没有在直观的层面上得到它们。

我有一个小应用程序正在从API中抓取数据,我可以轻松地console.log来自每个请求的响应,我的问题是我正在尝试收集数据并构建一个对象,以便在所有请求完成时保存到文件中。

我得到nodejs是一个单独的执行线程并且它不会阻塞但我无法弄清楚当所有内部请求完成时将回调放在哪里我可以console.log构建的对象。您将看到我的console.log行位于错误的位置并在内部请求的第一个响应之前执行。

分解

  1. 获取国家/地区数据
  2. 循环countryResponse并使用每个国家/地区ID来获取详细信息
  3. 将每个细节添加到数组中
  4. 所有请求完成后,将数组添加到对象。

const limit = require("simple-rate-limiter");

let request = limit(require("request")).to(1).per(200);


let options = {
    method: 'POST',
    url: 'https://myendpoint/details',
    headers: {
        'cache-control': 'no-cache',
        'Content-Type': 'application/json'
    },
    body: {
        "token": "TOKEN",
        "method": "countries"
    },
    json: true
};

global.package = {};
global.services = {};
let countryServices = [];

/*
    Country fetch
*/
request(options, function (err, response, countryResponse) {
    if (err) {}

    package.countries = countryResponse;

    countryResponse.forEach(function (entry) {

        let innerOptions = {
            method: 'POST',
            url: 'https://myendpoint/details',
            headers: {
                'cache-control': 'no-cache',
                'Content-Type': 'application/json'
            },
            body: {
                "token": "TOKEN",
                "method": "services"
            },
            json: true
        };
        //THIS LINE OMG
        //let countryServices = [];

        innerOptions.body.countryCode = entry.countryCode;

        request(innerOptions, function (err, response, innerResponse) {
            if (err) {}

            countryServices.push(innerResponse);
            console.log(" inner response " + entry.countryCode + ' : ' + JSON.stringify(innerResponse, null, ""));

        });//END innerResponse
    });//END countryResponse.forEach
    services = countryServices;
    console.log(JSON.stringify(package, null, ""));
    console.log(JSON.stringify(countryServices, null, ""));
});//END orderResponse

countryResponse

[
    {
        "countryCode": 1,
        "countryName": "Virgin Islands (U.S.)"
    },
    {
        "countryCode": 7,
        "countryName": "Russian Federation"
    }
]

内部响应

[
    {
        "Type": "1",
        "id": 2
    },
    {
        "Type": "2",
        "id": 3
    }
]
node.js callback
2个回答
1
投票

最简洁直接的方法可能是async/await方式。您可以手动宣传request并用简单的延迟替换simple-rate-limiter依赖:

'use strict';

const request = require('request');

function promisifiedRequest(options) {
  return new Promise((resolve, reject) => {
    request(options, (err, response, body) => {
      if (err) reject(err);
      else resolve(body);
    });
  });
}

function delay(ms) {
  return new Promise((resolve) => { setTimeout(resolve, ms); });
}

const options = {
  method: 'POST',
  url: 'https://myendpoint/details',
  headers: {
    'cache-control': 'no-cache',
    'Content-Type': 'application/json',
  },
  body: {
    token: 'TOKEN',
    method: 'countries',
  },
  json: true,
};

(async function main() {
  try {
    const countryResponse = await promisifiedRequest(options);

    const innerRequests = [];
    for (const entry of countryResponse) {
      const innerOptions = {
        method: 'POST',
        url: 'https://myendpoint/details',
        headers: {
          'cache-control': 'no-cache',
          'Content-Type': 'application/json',
        },
        body: {
          token: 'TOKEN',
          method: 'services',
          countryCode: entry.countryCode,
        },
        json: true,
      };

      const innerRequest = promisifiedRequest(innerOptions);
      innerRequests.push(innerRequest);
      await delay(200);
    }

    const countryServices = await Promise.all(innerRequests);
    console.log(JSON.stringify(countryServices, null, ''));
  } catch (err) {
    console.error(err);
  }
})();

如果您需要更多背景或需要扩展应用程序(添加具有更复杂速率限制的并行请求),这些材料可能会有所帮助:

Stackoverflow: How do I return the response from an asynchronous call?

Stackoverflow: Why is my variable unaltered after I modify it inside of a function?

Handling asynchronous operations in parallel

Back-off and retry using JavaScript arrays and promises


1
投票

代码末尾的console.logs不会等待forEach触发的所有异步操作在运行之前完成。您需要引入某种机制来等待forEach触发的所有函数来完成其请求。

如果你想坚持使用回调,那么你可以看看使用eachasync方法,它将为你处理这种情况。

这个问题通常使用Promisesasync/await来处理。如果您使用了promise based interface to request,假设有一个相当最新版本的Node.js(省略选项),您的示例将看起来像这样:

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

async function run() {
  const options = {};
  const countryServices = [];
  const countryResponse = await request(options);

  for (const country of countryResponse) {
    const innerOptions = {};
    const innerResponse = await request(innerOptions);
    countryServices.push(innerResponse);
  }

  console.log(countryServices);
}

run();

这比使用回调更清楚,for-of循环的行为与您期望的完全相同。

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