티스토리 뷰

반응형

 

Promise에는 5가지 정적 메서드가 있다.

  • Promise.all
  • Promise.allSettled
  • Promise.race
  • Promise.resolve
  • Promise.reject

이 중 가장 유용하고 많이 쓰이는 것은 Promise.all 일 것이다. 그래서 공부겸 직접 구현해보았다.

Promise.all 직접 구현해보기

function all(iterable = []) {
  if (!iterable || !iterable.length) return Promise.resolve([]);

  return new Promise((resolve, reject) => {
    const result = iterable.map(() => ({ state: 'pending', value: undefined }));
    
    iterable.forEach((iter, idx) => {
      if (iter instanceof Promise === false) {
        result[idx] = { state: 'fulfilled', value: iter };
        const isDone = !result.some(v => v.state === 'pending');
        if (isDone) resolve(result.map(v => v.value));
        return;
      }
      
      iter.then(value => {
        result[idx] = { state: 'fulfilled', value };
        const isDone = !result.some(v => v.state === 'pending');
        if (isDone) resolve(result.map(v => v.value));
      }).catch(err => {
        reject(err)
      });
    });
  });
}

처음에는 감을 못잡고 좀 해맸었는데 mdn 문서를 읽으면서 넘겨주는값과 반환값들을 살펴보면서 한땀한땀 짜기 시작하니 생각보다 금방 짜졌다. 그러나 mdn에 주어진 예제들을 가지고 하나씩 실행하다보니 예외가 생각보다 많았고 몇 번의 리팩토링 끝에 완성(?)하였다.

 

위의 코드에서 (*) 부분은 원래 생각지 못했던 부분인데, all 함수에 순회객체를 넘길 때 Promise 인스턴스가 하나도 없을 경우 resolve가 되지 않아 then으로 값이 넘어오지 않고 계속 pending상태로 남아있는 경우가 발생해서 추가하게 된 부분이다.

 

그리고 처음에는 아무생각없이 result.length로 판단하고 resolve를 실행해주었는데 비동기함수 인자를 마지막에 넘기는 바람에 실행이 너무나도 잘되서 그게 끝인줄 알았는데... 다행히 블로그 작성을 완료하기 전에 순서를 바꿔 테스트 해보니 안되는걸 발견하고 이후로 엄청 고민했던거 같다.

그래서 결국 해결책으로 사용한 방법이 Promise 객체처럼 상태값을 활용하는 것!

{ state: 'pending', value: undefined } 이 객체를 처음 이터러블 length 만큼 초기화 해두고 상태값을 판단해서 모두 fulfilled되면 resolve하는 것으로 해결해 주었다.

 

코드는 깃헙에 올려두었다.

실행해보기

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('foo');
  }, 1000);
});

// 성공
all(['hi', p, Promise.resolve(33)])
  .then(values => console.log(values))
  .catch(reason => console.log(reason));
  
// 실패
all(['hi', p, Promise.resolve(33), Promise.reject(555)])
  .then(values => console.log(values))
  .catch(reason => console.log(reason));

// 콘솔에는 아래 순서대로 찍힌다.
// 555
// [ 'hi', 'foo', 33 ]

실행 결과

성공 케이스는 모든 실행이 완료되어야만 나오기 때문에 위 코드의 경우 setTimeout에 걸려서 실패 케이스보다 나중에 나온다.

 

해결하지 못한 예외케이스

mdn 문서에 나온 예시 대부분은 출력이 일치하는 것을 확인했는데 한 가지 정확하게 구현하지 못한 케이스가 있었다.

the stack is now empty 가 출력되기 전에는 Promise가 pending 상태여야 하는데 내가 만든 all 함수는 fulfilled 상태로 뱉는다.

Promise 인스턴스가 아닌 값도 Promise로 한번 감싸서 then 처리를 해주는걸까?

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함