React 프로젝트에서 Promise.allSettled 활용하기

React 프로젝트에서 Promise.allSettled 활용하기

React 또는 타입스크립트 프로젝트에서 Promise.allSettled를 활용해야 하는 이유, 그리고 어떻게 프로젝트에 적용했는지를 작성했습니다.

·

2 min read

Promise.all

프론트엔드에서 여러 개의 네트워크 요청을 처리할 때 Promise.all 또는 Promise.allSettled 메소드를 활용하여 동시에 처리할 수 있다.

Promise.all 메소드는 프로미스 배열을 인자로 받아서 하나의 프로미스를 반환하는데 이 프로미스가 이행(fulfill)되면 인자에 있던 각 프로미스의 결과 값을 배열 형태로 받을 수 있다.

  • 다음과 같은 상황에는 콘솔에 [1, 2, 3]이 출력된다.
const resolvedAll = await Promise.all([
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.resolve(3),
]);
console.log(resolvedAll);
  • 첫번째 fetchApis 함수는 각각의 요청을 async, await으로 순서대로 처리하기 때문에 시간이 오래 걸린다.

  • 두번째 fetchApis 함수는 Promise.all 메소드로 소요 시간을 단축시킬 수 있다.

const App = () => {
  const fetchApis = useCallback(async () => {
    const resp1 = await fetchApi1(args1);
    const resp2 = await fetchApi2(args2);
    const resp3 = await fetchApi3(args3);
    return [resp1, resp2, resp3];
  }, []);

  const fetchApis = useCallback(async () => {
    const responses = await Promise.all(
      fetchApi1(args1),
      fetchApi2(args2),
      fetchApi3(args3)
    );
  }, []);
};

Promise.all 메소드는 각각의 작업 중 하나라도 작업이 거부(reject)되면 아무것도 반환되지 않는다. 이때 메소드가 반환한 프로미스의 거부 사유는 첫번째 프로미스와 같다.

다음 상황에서 첫번째로 거부되는 프로미스는 Promise.reject(1)로, 콘솔에는 1이 출력된다.

try {
  const result = await Promise.all([
    Promise.reject(1),
    Promise.reject(2),
    Promise.reject(3),
    Promise.reject(4),
  ]);
} catch (error) {
  console.log(error);
}

Promise.allSettled

Promise.allSettled 메소드는 프로미스 배열을 받아서 하나의 프로미스를 반환한다는 것은 Promise.all과 같으나, 프로미스가 이행되면 인자로 주어진 배열의 각 프로미스에 대해 이행된 결과를 나타내는 배열을 받을 수 있다.

메소드가 반환한 프로미스가 이행되었을 때 각 요소는 다음과 같은 타입을 나타낸다.

  • 공통적으로 status라는 속성이 존재한다.

  • 이행되면 value로 각각의 프로미스가 이행된 값을 확인할 수 있다.

  • 거부된 프로미스는 reason으로 거부된 사유를 파악할 수 있다.

interface PromiseFulfilledResult<T> {
  status: "fulfilled";
  value: T;
}

interface PromiseRejectedResult {
  status: "rejected";
  reason: any;
}

type PromiseSettledResult<T> =
  | PromiseFulfilledResult<T>
  | PromiseRejectedResult;

프로젝트에서 Promise.allSettled 메소드를 적용할 때에는 이행된 결과만 필터링하는 작업이 필요했다.

  • 첫번째 예시처럼 filter 메소드로 필터링만 하거나

  • 두번째 처럼 reduce 메소드로 반환값을 토대로 추가 작업을 할 수 있었다.

const responses = await Promise.allSettled([
  Promise.resolve(0),
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.reject("REJECT 1"),
  Promise.reject("REJECT 2"),
  Promise.resolve(true),
  Promise.resolve(false),
]);
const fulfilledAll = responses.filter(
  (response) => response.status === "fulfilled"
);
const responses = await Promise.allSettled([
  fetchApi(0),
  fetchApi(1),
  fetchApi(2),
  fetchApi(3),
  fetchApi(4),
]);
const projects = responses.reduce((reduced, response) => {
  if (response.status === "rejected") {
    return reduced;
  }
  const id = createIdFromResponse(response);
  reduced = reduced.concat({
    id,
    ...response,
  });
  return reduced;
}, []);