Vishnu
Vishnu

Reputation: 724

Promise not getting resolved inside reduce array method javascript

I have large array of objects and filtered the objects based on the userID. Here is the code below.

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;
            }, {});

Expected Output :

{
  '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'
  }
}

I need to get picture from an api which is asynchronous, so used async await inside the reduce method. The problem here is it is always showing as Promise pending. If this was an array of object, then i can return Promise.all, but since this is object containing object how can i proceed with this inside reduce method? I need the exact same expected output.

Can somebody help me with this? Any help would be really appreciated.

Upvotes: 0

Views: 455

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370789

To use reduce while iterating over items asynchronously, you'd have to have the accumulator which gets passed from callback to callback to be a Promise. While this is possible, it'll make things pretty difficult to read, and introduces some unnecessary syntax noise.

Use a plain for loop instead:

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 };
}

If you really wanted to take the reduce route:

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
  });

Unless the getPic calls need to be made in serial, you could consider using Promise.all instead, to iterate through the whole array at once, rather than waiting on the resolution of the prior Promise before going onto the next.

If your API can handle 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 };
}));

Upvotes: 1

Related Questions