Создайте массив обещаний выборки, используя цикл for с JavaScript / ES6, который можно прочитать через Promise.all?

#javascript #api #asynchronous #foreach #synchronous

Вопрос:

Итак, чтобы не утомлять никого предысторией, мне нужно получить доступ к данным из нескольких API, чтобы запустить мой скрипт. Все данные должны быть загружены до того, как я выполню скрипт, что мне обычно удобно делать: я просто объявляю некоторые запросы на выборку, пишу Promise.all, затем продолжаю работу с функцией.

ОДНАКО я столкнулся с чем-то вроде путаницы с определенным API, который ограничивает количество результатов, которые я могу извлечь из одного запроса, до 100, и мне нужно запросить все результаты. Я не думал, что это имеет большое значение, поскольку я решил, что могу просто сделать пару дополнительных запросов, добавив «amp;page= X» в конец запроса.

Таким образом, план состоит в том, чтобы запросить общее количество страниц из API, а затем передать это в цикл for, чтобы отправить несколько запросов на выборку в массив обещаний (т. е. link://to/api/dataamp;page = 1, link://to/api/dataamp;page = 2 и т. Д.). Однако, когда я действительно пытаюсь создать этот массив с помощью цикла for, массив возвращается пустым. Вот моя работа:

 const dataUrlNoPage = 'link/to/api/dataamp;page=';
const totalPages = 3; //usually pulled via a function, but just using a static # for now

let apiRequestLoop = function(inp) {
  return new Promise(function(resolve){
    let promiseArray = [];
    for (let i = 1; i <= inp; i  ) {
      let dataUrlLoop = dataUrlNoPage   i;
      fetch(dataUrlLoop).then(function(response){
        promiseArray.push(response.json());
      })
     }
    resolve(promiseArray);
  })
}

let finalPromiseArray = apiRequestLoop(totalPages).then(result => {
  let requestArray = [apiRequest1,apiRequest2];
  //requestArray contains earlier fetch promises
  result.forEach(foo => {
    requestArray.push(foo);
    }
  );
  return requestArray;
});
 

Итак, что меня сбивает с толку, так это действительно цикл и то, как он не возвращает массив обещаний. Когда я смотрю на него в консоли, он отображается как пустой массив, но я могу развернуть его и увидеть обещания, которые я ожидал. Я вижу ответ «Значение ниже было оценено только что». Однако, независимо от того, сколько обещаний или .thens я пишу, массив фактически никогда не заполняется во время выполнения.

Что происходит? Могу ли я не генерировать обещания выборки через цикл for?

(Кроме того, чтобы немного сократить эту строку вопросов, да, API, к которому я пытаюсь получить доступ, — это WordPress. Оглядываясь вокруг, большинство людей предлагают создать пользовательскую конечную точку, но давайте предположим, что для целей этого проекта мне запрещено это делать.)

Ответ №1:

Здесь у вас есть несколько проблем.

Во-первых, у вас есть функция, предоставленная new Promise самой себе, содержащая создания обещаний. Не делайте этого! Это определенный антишаблон, который не поддерживает чистоту вашего кода.

Второй — это базовый бит кода:

 let promiseArray = [];
for (let i = 1; i <= inp; i  ) {
  let dataUrlLoop = dataUrlNoPage   i;
  fetch(dataUrlLoop).then(function(response){
    promiseArray.push(response.json());
  })
 }
resolve(promiseArray);
 

Это говорит:

  1. создайте пустой массив
  2. перебирайте другой массив, выполняя HTTP-запросы
  3. разрешите свое обещание с помощью пустого массива
  4. когда HTTP-запросы будут завершены, добавьте их в массив

Четвертый шаг всегда будет следовать за третьим шагом.

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

 let apiRequestLoop = function(inp) {
  let promiseArray = [];
  for (let i = 1; i <= inp; i  ) {
    let dataUrlLoop = dataUrlNoPage   i;
    promiseArray.push(fetch(dataUrlLoop).then(function(response) {
      return response.json();
    }));
  }
  return Promise.all(promiseArray);
}
 

или с помощью функции со стрелкой для очистки:

 let apiRequestLoop = function(inp) {
  let promiseArray = [];
  for (let i = 1; i <= inp; i  ) {
    let dataUrlLoop = dataUrlNoPage   i;
    promiseArray.push(fetch(dataUrlLoop).then(response => response.json()));
  }
  return Promise.all(promiseArray);
}
 

Комментарии:

1. Спасибо! Очень ценю, что вы рассказали мне об этом. Рефакторинг был приятным бонусом.

Ответ №2:

Несколько моментов:

  • вы хотите фактически поместить сами обещания в массив, а не отправлять в массив в .then() цепочке с обещанием.
  • вероятно, вы хотите пропустить создание нового обещания в своей функции. Просто получите массив всех обещаний из вашего цикла, затем верните a Promise.all в массив.

Вот так:

 let apiRequestLoop = function(inp) {
  let promiseArray = [];
  for (let i = 1; i <= inp; i  ) {
    let dataUrlLoop = dataUrlNoPage   i;
     promiseArray.push(fetch(dataUrlLoop))
  }
  return Promise.all(promiseArray);
}
 

В вашем последнем .then заявлении в finalPromiseArray вашим результатом будет массив результатов всех обещаний. вот так [response1, response2, response3, ...]

Смотрите Обещание.вся документация для получения более подробной информации.