Promise.all 사용 시 주의점

서버에서 받아온 다수의 image URL들을 파일 오브젝트 형태로 변환이 필요한 작업이 있었다.

파일 오브젝트 변환은 성공했지만 index 순서가 매우 중요한 image 배열들이 제 각각 랜덤으로 push 되는 문제가 있어

블로그 포스팅을 남긴다.


문제가 발생된 코드

   await Promise.all(
        data.images.map(
          async (
            el: { file: string; file_origin_name: string },
            idx: number,
          ) => {
           
           // file object 변환 함수
            const coverterItem = await imageURLtoFile(
              el.file,
              el.file_origin_name,
            );

            imagesConverter.push({
              id: idx,
              name: el.file_origin_name,
              file: coverterItem,
              change: true,
            });
          },
        ),
      );

Promise.all 메서드 안에서 image URL 값들이 담긴 배열들을 순차적으로 파일 오브젝트 변환을 시도하였다.

그 결과 아래의 사진과 같이 image URL index는 제 각각 내가 생성한 배열에(imagesConverter) push 되었다.

스크린샷 2023-04-16 22 39 06

Promise.all의 특징

스크린샷 2023-04-16 오후 5 00 36

Promise.all은 태스크 단위들을 절차적 수행이 아닌 병렬적 형태로 작업을 수행한다.

즉 내가 작성한 코드들은 promise.all 메서드 안에서 map 태스크 단위들이 파일 오브젝트 변환 과정의 딜레이 발생한다.

그러므로 image url 배열들이 빨리 처리된 순서대로 쌓이게 되어 index 순서가 뒤죽박죽 push 된 것이다.

개선된 코드

   const orderConverterPromiseList = data.images.map(
        async (el: { file: string; file_origin_name: string }, idx: number) => {
          const coverterItem = await imageURLtoFile(
            el.file,
            el.file_origin_name,
          );
          return {
            id: idx,
            name: el.file_origin_name,
            file: coverterItem,
            change: true,
          };
        },
      );

      const imagesConverterList: ImuchFile[] = await Promise.all(
        orderConverterPromiseList,
      );
  1. 우선 파일 오브젝트 형태로 변환하여 배열에(orderConverterPromiseList) 절차적으로 저장한다.
  2. 변환이 다 완료되었지만 promise 상태이므로 promise.all 메서드를 활용하여 비동기 처리 후 변수에(imagesConverterList) 할당한다.

스크린샷 2023-04-16 22 39 32

위 작성한 코드는 순서가 보장된 파일 오브젝트 배열을 갖을 수 있게 된다.

그러나 문제를 해결 했음에도 한가지 의혹이 있었는데 “promise.all 메서드면 다시 병렬적 처리가 되어 순서가 꼬이지 않을까?” 라는 궁금증이 생겼다.

분석한 결과

  promise.all은 병렬적 처리일지라도 배열의 작업 태스크가 매우 가벼운 형태로 일관성이 존재할 경우 결국 순서가 보장될 수 밖에 없다.

스크린샷 2023-04-16 오후 10 58 35

이미 내가 작성한 코드를 통해 보면 알 수 있듯, 가장 많은 시간이 소요되는 파일 오브젝트 변환 과정이 이미 완료된 시점_ 에서 promise.all 메서드가 비동기 처리를 하기 때문에 자연스레 태스크 단위 순서가 절차적으로 배열에 저장된다.