Добавить ключ в массив объектов в асинхронной функции

У меня есть маршрут API, который должен брать данные из двух источников, объединять данные в один объект и затем возвращаться. Проблема, с которой я сталкиваюсь, заключается в том, что я в основном застрял в асинхронном/ожидающем аду, и при нажатии на второй массив в блоке .then() второй массив с именем clone возвращается []. Как я могу сделать запрос API, объединить данные и вернуться к запрашивающей стороне по мере необходимости?

Получить код:

export default async function getProduct(product_id) {
  const product = await fetch(
    `${process.env.PRIVATE_APP_URL}/products/${product_id}.json`,
    {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
      },
    }
  ).then((result) => {
    return result.json();
  });
    return product.product;
}

Обработчик API:

const recharge_subscription_res = await rechargeAPI(
  "GET",
  `https://api.rechargeapps.com/subscriptions?customer_id=${recharge_customer.id}`
);

const closest_array = recharge_subscription_res.subscriptions.filter(
  (e) => e.next_charge_scheduled_at == closest_date
);

let clone = [];

closest_array.forEach((element) => {
  getProduct(element.shopify_product_id).then((product) => {
    element.shopify_product_handle = product.handle;
    element.shopify_product_image_url = product.image.src;
    clone.push(element);
  });
});

console.info(clone);

clone должен регистрироваться как массив объектов, таких как closest_array, но вместо этого регистрируется как пустой массив. Это не совсем похоже на другие, казалось бы, повторяющиеся вопросы, потому что обычно их функция не требует отправки данных промиса обратно во внешний источник. Большинство вопросов связаны с передним концом вещей. Моя ситуация связана с API Express.js. Любая помощь будет оценена по достоинству.

Вы регистрируете его еще до того, как к нему что-либо добавлено! getProduct является асинхронным.

CherryDT 04.04.2022 21:59

@CherryDT, возможно, мне следовало упомянуть об этом в посте, но возвращаемые данные из вызова также являются пустым массивом.

G.Rose 04.04.2022 22:29
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
2
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В вашем коде есть недостаток (в разделе, показанном ниже). У вас есть невыполненное обещание, которое вы забыли дождаться или вернуть. Когда вы регистрируетесь clone, ни одна из асинхронных getProduct операций еще не завершена, и ни один из элементов не был отправлен.

let clone = [];

closest_array.forEach((element) => {
  getProduct(element.shopify_product_id).then((product) => {
    element.shopify_product_handle = product.handle;
    element.shopify_product_image_url = product.image.src;
    clone.push(element);
  }); // FLAW:  dangling .then
});
console.info(clone); // FLAW: clone is not ready yet.

Я бы поставил примерно так:

let clone = await Promise.all(closest_array.map((element) =>
  getProduct(element.shopify_product_id).then((product) => {
    element.shopify_product_handle = product.handle;
    element.shopify_product_image_url = product.image.src;
    return element;
  })
));
console.info(clone);

Немного схематично изменить element таким, какой вы есть (я бы не стал), но таким образом все вызовы getProduct находятся в полете вместе для максимальной эффективности. Promise.all обрабатывает ожидание всех обещаний и помещает результат каждого в массив результатов, который затем можно ожидать как одно обещание, поскольку вызывающая функция является асинхронной.

Спасибо за ваш ответ, просто любопытно, как бы вы изменили element в этом сценарии. Чем больше отзывов я получу, тем лучше

G.Rose 05.04.2022 00:44

@ G.Rose Я думаю, что был удивлен, что вы создаете clone, который является копией closest_array, но со всеми теми же элементами (буквально теми же объектами), что заставляет меня задуматься, почему у вас вообще есть копия массива. С другой стороны, если бы каждый элемент клона был копировать каждого элемента в closest_array, тогда кажется разумным сделать копию. В вашем случае я думаю, что вы просто добавляете несколько свойств к каждому элементу, поэтому я не уверен, что вам вообще нужен новый clone новый массив. Трудно узнать ваши полные намерения только из того, что вы предоставили.

Wyck 05.04.2022 18:43

вы, вероятно, правы, я не думаю, что мне нужен клон. Сначала я установил его таким образом, потому что думал, что мне нужно нажать на мутировавший объект в foreach в массив клонов, чтобы избежать обещаний, но, конечно, это было не так.

G.Rose 05.04.2022 18:45
Ответ принят как подходящий

Первоначальная спецификация промиса использовала .then(), а новый синтаксис скрывает then с ожиданием. С точки зрения стиля имеет смысл выбрать стиль только один и придерживаться его.

В любом стиле есть небольшая проблема, связанная с созданием большого количества промисов в цикле. Функции итерации js (такие как map и forEach) принимают функции синхронный. Наиболее распространенный дизайн — создать набор промисов в синхронном цикле, а затем запустить их одновременно с Promise.all(). Принимая во внимание обе идеи...

Вы можете (но не обязаны) переписать свой сетевой запрос следующим образом...

// since we decorated "async" let's use await...
export default async function getProduct(product_id) {
  const url = `${process.env.PRIVATE_APP_URL}/products/${product_id}.json`;
  const options = { method: "GET", headers: { "Content-Type": "application/json" }};
  const result = await fetch(url, options);
  const product = await result.json();
  return product.product;
}

await не разрешено на верхнем уровне; его можно использовать только внутри асинхронной функции. Здесь я придумаю имя и угадаю параметр

async function rechargeAndLookupProduct(recharge_customer) {
  const base = 'https://api.rechargeapps.com/subscriptions';
  const query = `customer_id=${recharge_customer.id}`;
  const recharge_subscription_res = await rechargeAPI("GET",`${base}?${query}`);

  const closest_array = recharge_subscription_res.subscriptions.filter(e =>
    e.next_charge_scheduled_at == closest_date
  );

  // here's the important part: collect promises synchronously
  // execute them together with Promise.all()
  const promises = closest_array.map(element => {
    return getProduct(element.shopify_product_id)
  });
  const allProducts = await Promise.all(promises);
  // allProducts will be an array of objects that the promises resolved to
  const clones = allProducts.map((product, i) => {
    // use Object.assign so we'll really have a "clone"
    let closest = Object.assign({}, closest_array[i]);
    closest.shopify_product_handle = product.handle;
    closest.shopify_product_image_url = product.image.src;
    return closest;
  });
  // if I didn't make any typos (which I probably did), then
  // clones ought to contain the result you expect
  console.info(clones);
}

Другие вопросы по теме