Используя nodejs, как я могу вернуть объект, содержащий все данные из пакета вызовов API в цикле? - PullRequest
0 голосов
/ 20 февраля 2019

Мне известны замыкания и обратные вызовы в JavaScript, но очевидно, что я не получаю их на интуитивном уровне.

У меня есть небольшое приложение, которое собирает данные из API, и я могу легко console.log отвечать на каждый запрос, моя проблема в том, что я пытаюсь собрать данные и создать объект для сохранения вфайл, когда все запросы завершены.

Я получаю, что nodejs - это один поток выполнения, и он не блокируется, но я не могу понять, куда поместить обратные вызовы, когда все внутренние запросы завершены. Iможет console.log построенный объект.Вы увидите, что мои console.log строки находятся не в том месте и выполняются до первого ответа от внутреннего запроса.

Разбивка

  1. Извлечение данных о стране
  2. Зацикливание на countryResponse и использование каждого идентификатора страны для извлечения деталей
  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"
    }
]

innerResponse

[
    {
        "Type": "1",
        "id": 2
    },
    {
        "Type": "2",
        "id": 3
    }
]

Ответы [ 2 ]

0 голосов
/ 20 февраля 2019

console.log s в конце вашего кода не будет ожидать завершения всех асинхронных операций, запущенных вашим forEach, прежде чем они запустятся.Вам нужно будет ввести какой-то механизм, который ожидает, пока все функции, запущенные forEach, завершат свои запросы.

Если вы хотите использовать обратные вызовы, вы можете взглянуть на что-то вроде each метода async , который справится с такой ситуацией для вас.

Эта проблема обычно решается с помощью Promises и async / await .Если бы вы использовали интерфейс, основанный на обещаниях, для запроса , ваш пример выглядел бы примерно так, предполагая довольно актуальную версию 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 ведет себя именно так, как вы ожидаете.

0 голосов
/ 20 февраля 2019

Самый краткий и простой способ сделать это может быть 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: как мне вернуть ответ от асинхронного вызова?

Stackoverflow: почему моя переменная не изменилась после того, как я изменил ее внутрифункция?

Параллельная обработка асинхронных операций

Откат и повторная попытка с использованием массивов и обещаний JavaScript

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...