5

I have a method that receives a profiles array and I have to map for each profile and inside this map I have to map again in the photos propriety, which contains the pictures ids for requesting to an API for getting this picture.

The question is, where can I safely access this profiles array with their loaded photos for each respective profile?

profiles.map((profile, i) => {
    let photos = []

    Promise.all(profile.photos.map(async idPhoto => { 
        const res = await fetch(...)
        const img = await res.blob()
        photos.push(img)
    }))

    .then(() => profiles[i].photos = [...photos])
})
1
  • 1
    profiles[i].photos is not the same as profile.photos? Commented Jun 14, 2019 at 0:17

2 Answers 2

2

With the outer map function the way it currently is, the Promise.all() calls are discarded, so there is no way for your code to detect when they are complete.

However, since you also do not appear to be using the return value of the outer map, we can make it return an array of Promises that resolve when the inner their array of Promises is all resolved. And then we can use the same Promise.all(array.map()) pattern as we use for the inner map.

const photoRequests = profiles.map(async (profile, i) => {
      let photos = []

      await Promise.all(profile.photos.map(async idPhoto => { 

        const res = await fetch(...)
        const img = await res.blob()
        photos.push(img)
      }));

      profiles[i].photos = [...photos];
})

// And now...

await Promise.all(photoRequests); 
// After this it is safe to access.

// Or, if the outer map is not in an async method:

Promise.all(photoRequests).then(() => {
  // It is safe to access profiles here
});

I've refactored the outer map to be an async function (aids readability IMO), but you can put it back if you prefer. Just have the outer map function return the result of the Promise.all call.

As to what else could be improved here, the having variables photos and profile.photos is a little confusing, so consider renaming photos. Also make it const while you're at it, as it's never reassigned.

Unless there's some other code that manipulates the photos array, the array spread syntax is not needed. Same for the index variable. Final code might look something like:

const photoRequests = profiles.map(async profile => {
      const loadedPhotos = []

      await Promise.all(profile.photos.map(async idPhoto => { 

        const res = await fetch(...)
        const img = await res.blob()
        loadedPhotos.push(img)
      }));

      profile.photos = loadedPhotos;
})


await Promise.all(photoRequests); 

Or you could use the fact that Promise.all resolves to an array containing the resolve values of the individual promises it received:

const photoRequests = profiles.map(async profile => {

      profile.photos = await Promise.all(
        profile.photos.map(async idPhoto => { 

          const res = await fetch(...)
          return res.blob()

        })
      );
})


await Promise.all(photoRequests); 
Sign up to request clarification or add additional context in comments.

7 Comments

But where is the return from the inner map ?
@YoussefMuhamad There is not need for a return, since we are using the return values to control timing and nothing else. Async methods even without return statements return promises that resolve when execution is complete.
@YoussefMuhamad Actually that gives me another idea, another edit incoming
The return still [Promise{<resolved>: undefined}, Promise{<resolved>: undefined}] when logging photoResquests after Promise.all()
OOOOOOOH but each profile.photos of profiles has its respective Blobs, that's awesome!!!!!
|
1

I think it would be better to separate each mapping into its own function, it makes it easier to read. I refactored your code into this:

    let fetchPhoto = async (photoId) => {
        // const res = await fetch(...);
        // return res.blob();
        return { imageData: photoId } // mock blob result
    };

    let mapPhotoIdToImage = async (profile) => {
        let photos = profile.photos.map(fetchPhoto)
        photos = await Promise.all(photos);
        profile.photos = photos;
        return profile;
    };

    let profileList = [{photos: ['id1', 'id2']}];

    let result = await profileList.map(mapPhotoIdToImage);

result:

[{ photos: [ { imageData: 'id1' }, { imageData: 'id2' } ] }]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.