async Array.map () внутри другого вызова карты - PullRequest
4 голосов
/ 14 июня 2019

У меня есть метод, который получает массив профилей, и я должен отобразить для каждого профиля, и внутри этой карты мне нужно снова отобразить карту в соответствии с фотографиями, которые содержат идентификаторы изображений для запроса API для получения этого изображения.

Вопрос в том, где я могу безопасно получить доступ к этому массиву профилей с их загруженными фотографиями для каждого соответствующего профиля?

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

Ответы [ 2 ]

1 голос
/ 14 июня 2019

Я думаю, что было бы лучше разделить каждое отображение на его собственную функцию, это облегчает чтение. Я переделал твой код в это:

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

результат:

[{ photos: [ { imageData: 'id1' }, { imageData: 'id2' } ] }]
1 голос
/ 14 июня 2019

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

Однако, поскольку вы также, похоже, не используете возвращаемое значение внешней карты, мы можем заставить его возвращать массив Обещаний, которые разрешаются, когда внутренний их массив Обещаний разрешен.И тогда мы можем использовать тот же шаблон Promise.all(array.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
});

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

Что касается того, что еще можно улучшить здесь, наличие переменных photos и profile.photos немного сбивает с толку, поэтому рассмотрите переименование photos.Также сделайте его const, пока он у вас, так как он никогда не переназначается.

Если нет другого кода, который манипулирует массивом photos, синтаксис распространения массива не нужен.То же самое для индексной переменной.Окончательный код может выглядеть примерно так:

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

Или вы можете использовать тот факт, что Promise.all разрешает массив, содержащий значения разрешения отдельных полученных обещаний:

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); 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...