async-await은 태스크의 순서가 보장되어야 할 때는 분명 강력하지만, 각각의 태스크가 서로 연관성이 없는 작업일 때는 앞의 작업이 끝날 때까지 기다릴 필요가 없다. 따라서 우리는 이때 다른 방법을 택해야한다.
Promise.all은 여러 비동기 동작(프라미스)을 하나로 묶어 하나의 promise 처럼 관리할 수 있게 해준다. 배열 안 promise가 모두 처리되면 새로운 promise가 이행되는데, 배열 안 프라미스의 결괏값을 담은 배열이 새로운 promise의 result가 된다.
const storeReviews = StoreReviewModel.find({ store: storeUUID, deletedAt: null });
const storeLikes = StoreLikeModel.find({ store: storeUUID, deletedAt: null })
가게에 대한 리뷰와, 가게에 대한 좋아요 목록들을 DB에서 가져올때 두개가 다른 테이블에 존재한다면 굳이 리뷰 목록들을 가져올때까지 기다렸다가 좋아요 목록들을 가져올 필요가 없다.
const [storeReviews, storeLikes] = await Promise.all([
StoreReviewModel.find({ store: storeUUID, deletedAt: null }),
StoreLikeModel.find({ store: storeUUID, deletedAt: null })
]);
이렇게 promise.all을 통해 가게에 대한 리뷰와 좋아요를 동시에 가져올 수 있다.
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("에러 발생!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
])
Promise.all에 전달되는 promise 중 하나라도 거부되면, Promise.all이 반환하는 promise는 에러와 함께 바로 거부된다. promise가 하나라도 거부되면 Promise.all은 즉시 거부되고 배열에 저장된 다른 프라미스의 결과는 완전히 무시된다. 이행된 프라미스의 결과도 물론 무시된다.
Promise.all은 프라미스가 하나라도 거절되면 전체를 거절한다. 따라서, 프라미스 결과 중 하나라도 없으면 안되는 상황에서는 promise.all을 사용한다.
const [storeReviews, storeLikes] = await Promise.allSettled([
StoreReviewModel.find({ store: storeUUID, deletedAt: null }),
StoreLikeModel.find({ store: storeUUID, deletedAt: null })
]);
반면, Promise.allSettled는 모든 프라미스가 처리될 때까지 기다린다.
[
{status: 'fulfilled', value: ...응답...},
{status: 'fulfilled', value: ...응답...},
{status: 'rejected', reason: ...에러 객체...}
]
반환되는 배열은 위와 같은 요소를 갖는다. 여러 요청 중 하나가 실패해도 다른 요청 결과는 여전히 유효하다. 여러개의 프로미스에서 모든 결과가 필요하지 않는 상황에서는 Promise.allSettled를 사용하도록 하자.
const fast = await Promise.race([
StoreReviewModel.find({ store: storeUUID, deletedAt: null }),
StoreLikeModel.find({ store: storeUUID, deletedAt: null })
]);
Promise.race는 Promise.all과 비슷하다다. 다만 가장 먼저 처리되는 프라미스의 결과(혹은 에러)를 반환한다. 위의 코드에서는 둘중에 먼저 실행되는 쿼리의 결과가 저장되게 된다.