将键添加到异步函数中的对象数组

Add key to array of objects in async function

我有一个 api 路由需要从两个来源获取数据,将数据合并到一个对象中,然后 return。我遇到的问题是我基本上陷入了 async/await 地狱,当推送到 .then() 块中的第二个数组时,第二个数组名为 clone returns []。如何发出 api 请求,合并数据并根据需要 return 给请求者?

获取代码:

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.log(clone);

clone 应该像 closest_array 一样记录为对象数组,但记录为空数组。这与其他看似重复的问题并不完全相同,因为通常它们的功能不需要将承诺的数据发送回外部源。大多数问题都与事物的前端有关。我的情况是 Express.js API。任何帮助将不胜感激。

您的代码存在缺陷(在下面显示的部分中)。你有一个悬而未决的承诺,你忘了等待或 return。 当您记录 clone 时,none 的异步 getProduct 操作已经完成,并且 none 的元素已被推送。

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.log(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.log(clone);

按照你现在的方式修改 element 有点粗略(我不会),但这样 getProduct 调用会一起进行以实现最大效率。 Promise.all 处理等待所有承诺并将每个结果放入结果数组中,然后您可以将其作为单个承诺等待,因为调用函数是异步的。

原来的 promise 规范使用 .then(),新语法隐藏了 then's with await。 Style-wise,选择就一种风格是有意义的。

在任何一种风格中,在循环中创建许多 promise 都存在一些挑战。 js 迭代函数(如 mapforEach)采用 synchronous 函数。最常见的设计是在同步循环中创建一组承诺,然后 运行 它们与 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 不允许出现在 top-level;它只能在异步函数中使用。这里我自己编个名字猜一下参数

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.log(clones);
}