在另一个地图调用中异步Array.map()

时间:2019-06-13 23:59:43

标签: javascript async-await

我有一个接收配置文件数组的方法,我必须为每个配置文件进行映射,并且在此映射中,我必须在照片属性中再次进行映射,其中包含用于请求API以获得此图片的图片ID。 / p>

问题是,我在哪里可以安全地访问此配置文件阵列及其每个配置文件的已加载照片?

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 个答案:

答案 0 :(得分:2)

我认为将每个映射划分为自己的函数会更好,这样更易​​于阅读。我将您的代码重构为:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="@color/colorBlack">
    </LinearLayout>

     <!--It will be on top of linear layout-->
    <TextView
        android:textColor="@color/white"
        android:text="test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</androidx.constraintlayout.widget.ConstraintLayout>

结果:

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

答案 1 :(得分:1)

使用外部map函数(当前方式),Promise.all()调用将被丢弃,因此您的代码无法检测它们何时完成。

但是,由于您似乎也没有使用外部映射的返回值,因此我们可以使它返回一个Promises数组,当内部的Promises数组全部解析后,该数组便会解析。然后我们可以使用与内部地图相同的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的可读性),但是您可以根据需要将其放回去。只需让外部map函数返回Promise.all调用的结果即可。

关于这里还有什么可以改进的地方,变量photosprofile.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);