
자바스크립트로 비동기 작업을 처리할 때, 여러 개의 비동기 요청을 동시에 관리해야 하는 상황을 마주하게 된다. 여러 개의 API를 호출할 때 그 결과를 효율적으로 처리하는 방법은 애플리케이션의 성능과 사용자 경험에 큰 영향을 미치게 되므로 프론트엔드 개발에 있어 중요한 부분이라 할 수 있다!
얼마 전, API를 순차적으로 호출하던 코드에서 Promise.allSettled 를 사용하여 동시에 호출하는 코드로 리팩토링한 경험이 있다. Promise.allSettled의 사용법과 장점에 대해 알아보고, Promise.all과의 차이점은 무엇인지 정리해보았다.
Promise.allSettled는 ES2020에 도입된 메서드로, 여러 개의 프로미스를 동시에 처리하고 각각의 결과(성공 또는 실패)를 한꺼번에 받아볼 수 있게 해준다. Promise.all과는 달리, 모든 프로미스가 완료될 때까지 기다리며, 각 프로미스의 상태와 결과를 객체 형태로 반환한다.
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject('Error');
const promise3 = Promise.resolve(3);
Promise.allSettled([promise1, promise2, promise3])
.then((results) => results.forEach((result) => console.log(result)));
위 코드의 출력은 다음과 같다.
{ status: 'fulfilled', value: 1 }
{ status: 'rejected', reason: 'Error' }
{ status: 'fulfilled', value: 3 }
이처럼 각 프로미스의 상태와 결과를 확인할 수 있다.
위와 같은 3개의 API를 순차적으로 호출할 때와, Promise.allSettled를 사용해 동시에 호출할 때의 응답 시간을 비교해보자.
async function fetchSequentially() {
try {
const response1 = await fetch('https://api.example.com/data1');
const data1 = await response1.json();
const response2 = await fetch('https://api.example.com/data2');
const data2 = await response2.json();
const response3 = await fetch('https://api.example.com/data3');
const data3 = await response3.json();
return [data1, data2, data3];
} catch (error) {
console.error('Error fetching data:', error);
}
}
이 방식은 각 API 호출이 이전 호출의 완료를 기다린 후 다음 호출을 시작한다. 따라서 전체 소요 시간은 각 API 응답 시간의 합이 된다.
따라서,총 1 + 2 + 3 = 6초가 소요되게 된다.
async function fetchAllSettled() {
const promises = [
fetch('https://api.example.com/data1').then(res => res.json()),
fetch('https://api.example.com/data2').then(res => res.json()),
fetch('https://api.example.com/data3').then(res => res.json())
];
const results = await Promise.allSettled(promises);
return results;
이 방식은 모든 API 호출을 동시에 시작하므로, 전체 소요 시간은 가장 오래 걸리는 API 응답 시간과 비슷하다.
따라서, 총 소요시간은 3초가 된다.
Promise.allSettled와 Promise.all은 모두 여러 프로미스를 동시에 처리할 수 있지만, 중요한 차이점이 있다.
async function fetchWithPromiseAll() {
const promises = [
fetch('https://api.example.com/data1').then(res => res.json()), // 성공
fetch('https://api.example.com/data2').then(res => res.json()), // 실패
fetch('https://api.example.com/data3').then(res => res.json()) // 성공
];
try {
const results = await Promise.all(promises);
console.log('Promise.all 결과:', results);
} catch (error) {
console.error('Promise.all 에러:', error);
}
}
fetchWithPromiseAll();
Promise.all 에러: Error: Failed to fetch
async function fetchWithPromiseAllSettled() {
const promises = [
fetch('https://api.example.com/data1').then(res => res.json()), // 성공
fetch('https://api.example.com/data2').then(res => res.json()), // 실패
fetch('https://api.example.com/data3').then(res => res.json()) // 성공
];
const results = await Promise.allSettled(promises);
console.log('Promise.allSettled 결과:', results);
}
fetchWithPromiseAllSettled();
Promise.allSettled 결과: [
{ status: 'fulfilled', value: { /* API 1 데이터 */ } },
{ status: 'rejected', reason: Error: Failed to fetch },
{ status: 'fulfilled', value: { /* API 3 데이터 */ } }
]
단, 각 호출의 순서가 중요한 경우에는 순차 호출이 유리할 수 있다.