React 프로젝트에서 Promise.allSettled 활용하기
React 또는 타입스크립트 프로젝트에서 Promise.allSettled를 활용해야 하는 이유, 그리고 어떻게 프로젝트에 적용했는지를 작성했습니다.
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;
}, []);