【问题标题】:Promise not getting resolved inside reduce array method javascript承诺在减少数组方法javascript中没有得到解决
【发布时间】:2020-09-03 07:17:48
【问题描述】:

我有大量对象,并根据用户 ID 过滤了对象。这是下面的代码。

const filteredArr = LargeArr.Items.reduce(
            async(acc, { attributes: { dob, name, picture} = { dob: null, name: null, picture: null }, userID }) => {
                let pic = null;
                if (picture) { pic = await getPic(picture); } // async here
                acc[userID] = { name, userID, pic, dob };
                return acc;
            }, {});

预期输出:

{
  '1595232114269': {
    name: 'Mark Status',
    userID: '1595232114269',
    picture: 'mark-status.jpg',
    dob: '2020-08-10'
  },
  '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d23d': {
    name: 'Jack Thomas',
    userID: '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d23d',
    picture: 'jack-thomas.jpg',
    dob: '1990-12-20'
  },
  '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d47p': {
    name: 'Petro Huge',
    userID: '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d47p',
    picture: 'petro huge.jpg',
    dob: '1856-12-20'
  },
  '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d55j': {
    name: 'Mark Henry',
    userID: '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d55j',
    picture: 'mark-henry.jpg',
    dob: '2005-12-29'
  }
}

我需要从一个异步的 api 中获取图片,所以在 reduce 方法中使用了 async await。这里的问题是它总是显示为 Promise pending。如果这是一个对象数组,那么我可以返回 Promise.all,但由于这是包含对象的对象,我如何在 reduce 方法中继续执行此操作?我需要完全相同的预期输出。

有人可以帮我解决这个问题吗?任何帮助将不胜感激。

【问题讨论】:

  • acc 将在第一次迭代后成为一个承诺(因为async 函数返回一个承诺)。
  • 为什么变量名filteredArr的内容是对象?顾名思义,它存储Array.prototype.filter() 调用的结果。
  • @FelixKling 是的,你能告诉我我们该怎么做吗?
  • 您可以先使用 Promise.all() 进行所有必要的 API 调用,然后使用 .reduce 并传递同步函数。
  • 只是不要在这里使用reduce :) 它使事情变得不必要地复杂。

标签: javascript node.js ecmascript-6 async-await


【解决方案1】:

要在异步迭代项目时使用reduce,您必须让从回调传递到回调的累加器成为Promise。虽然这是可能的,但它会让事情变得非常难以阅读,并引入一些不必要的语法噪音。

改用普通的for 循环:

const filteredArr = {};
for (const item of LargeArr.Items) {
  const { attributes: { dob, name, picture} = { dob: null, name: null, picture: null } } = item;
  const pic = picture ? await getPic(picture) : null;
  filteredArr[userID] = { name, uesrID, pic, dob };
}

如果你真的想走reduce 路线:

LargeArr.Items.reduce(
  (acc, { attributes: { dob, name, picture} = { dob: null, name: null, picture: null }, userID }) => {
    return acc.then(async (acc) => {
      let pic = null;
      if (picture) { pic = await getPic(picture); } // async here
      acc[userID] = { name, userID, pic, dob };
      return acc;
    });
  }, Promise.resolve({})
)
  .then((filteredArr) => {
    // do stuff with filteredArr
  });

除非getPic 调用需要串行进行,否则您可以考虑使用Promise.all 来一次遍历整个数组,而不是等待前一个 Promise 的解决方案之后再进行下一个。

如果你的 API 可以处理Promise.all:

const filteredArr = {};
await Promise.all(LargeArr.Items.map(async (item) => {
  const { attributes: { dob, name, picture} = { dob: null, name: null, picture: null } } = item;
  const pic = picture ? await getPic(picture) : null;
  filteredArr[userID] = { name, uesrID, pic, dob };
}));

【讨论】:

  • 第二个例子不起作用,因为acc 在第一次迭代中不是一个承诺。但是,如果您使用acc = await acc; 而不是return acc.then(...)(并创建外部函数async),那么它应该可以工作。
  • @certainperformance inside for of const 属性解构,我认为缺少一个大括号对吗?
  • @Vishnu 是的,已修复。嵌套解构使事情难以读写,可能希望尽可能避免它
  • @CertainPerformance 我不明白你对 getPic 调用串行和使用 Promise.all 的看法。如果你能解释一下就太好了。谢谢。
  • @Vishnu Say getPic 需要 1 秒。然后,如果数组中有 5 张图片,您当前的方法和 for..of 总共需要 5 秒。但是如果你使用Promise.all,你可以一次发送所有请求,所以总共只需要1秒。但是如果getPic 需要串行运行(一个接一个),那么坚持使用for 循环而不是Promise.all
猜你喜欢
  • 2022-01-16
  • 2021-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-18
  • 1970-01-01
  • 2021-09-04
  • 2021-05-01
相关资源
最近更新 更多