Promise의 정적 메서드를 통한 비동기 함수의 동시성 처리

May 2, 2021 ˑ 6min read

thumbnail

개요

ES6(ECMAScript2015)부터 Promise가 지원되기 시작하면서 비동기 함수를 편리하게 다룰 수 있게 되었습니다. Promise는 단어의 의미대로 어떠한 값을 미래에 돌려준다는 약속을 의미하는데요. Promise의 등장 이후 비동기 함수를 동시에 실행할 수 있도록 도와주는 정적 메서드들이 제공되기 시작했습니다. 이번 글에서는 Promise의 동시성 처리를 위해 제공되는 정적 메서드에 대해 알아보겠습니다.

Important

동시성(concurrency)병렬성(parallelism)은 비슷한 개념이지만, 엄밀히 보면 차이가 있습니다.

JavaScript는 기본적으로 싱글 스레드(Single-threaded)로 동작하기 때문에 Promise.all을 사용한다고 해서 실제로 병렬 실행이 일어나는 것은 아닙니다. 이벤트 루프를 통해 짧은 시간에 번갈아 실행하는 방식으로 함수들이 병렬로 실행되는 것처럼 보이는 동시성 처리를 지원합니다.

Note

여기서 Promise의 완료는 성공(resolve)또는 실패(reject)되어 결과값이나 에러를 반환한 상태를 의미합니다.

Promise.all

Promise가 지원되기 시작한 ES6부터 Promise.all 메서드도 함께 지원되기 시작했습니다. 인자로 받은 Promise 배열이 모두 성공(resolve)한 경우 결과값의 배열을 반환하며, 하나의 Promise라도 실패(reject)한 경우 즉시 완료되어 에러를 발생합니다. 해당 메서드는 모든 비동기 작업이 반드시 성공해야 다음 작업이 진행될 수 있는 경우 유용하게 사용할 수 있습니다.

promise-all.js
// e.g. 성공 케이스 const p1 = () => Promise.resolve(1); const p2 = () => Promise.resolve(2); const p3 = () => Promise.resolve(3); Promise.all([p1(), p2(), p3()]) .then(results => console.log('결과:', results)) .catch(error => console.error('에러:', error)); // [console] "결과: [1, 2, 3]" // e.g. 실패 케이스 const p1 = () => Promise.resolve(1); const p2 = () => Promise.resolve(2); const p3 = () => Promise.reject('에러 발생'); Promise.all([p1(), p2(), p3()]) .then(results => console.log('결과:', results)) .catch(error => console.error('에러:', error)); // [console] "에러: 에러 발생"

Promise.race

Promise.all과 함께 ES6 부터 제공된 Promise.race는 가장 먼저 완료된 Promise의 결과값을 반환하는 특징을 갖고 있습니다. 가장 먼저 실패(reject)한 경우에도 즉시 완료처리되어 에러를 발생하기 때문에 예외처리를 주의해야합니다.

promise-race.js
// e.g. 성공 케이스 const p1 = new Promise(resolve => setTimeout(() => resolve(1), 3000)); const p2 = new Promise(resolve => setTimeout(() => resolve(2), 2000)); const p3 = new Promise(resolve => setTimeout(() => resolve(3), 1000)); Promise.race([p1, p2, p3]) .then(result => console.log('결과:', result)) .catch(error => console.error('에러:', error)); // [console] 3 // e.g. 실패 케이스 const p1 = new Promise(resolve => setTimeout(() => resolve(1), 3000)); const p2 = new Promise(resolve => setTimeout(() => resolve(2), 2000)); const p3 = new Promise(resolve => setTimeout(() => reject('에러 발생'), 1000)); Promise.race([p1, p2, p3]) .then(result => console.log('결과:', result)) .catch(error => console.error('에러:', error)); // [console] "에러: 에러 발생"

Promise.allSettled

Promise.allSettled는 ES11에 추가된 정적 메소드로 인자로 전달된 Promise 배열의 성공/실패 여부와 상관없이 항상 결과값을 반환합니다. 실패한 Promise도 결과값을 확인할 수 있기 때문에 개별적인 처리가 필요한 경우 유용하게 사용할 수 있습니다.

promise-all-settled.js
const p1 = Promise.resolve(1); const p2 = Promise.reject('에러 발생'); const p3 = Promise.resolve(3); Promise.allSettled([p1, p2, p3]) .then(results => console.log('결과:', results)); /* [console] [ { status: "fulfilled", value: 1 }, { status: "rejected", reason: "에러 발생" }, { status: "fulfilled", value: 3 } ] */

Promise.any

Promise.any는 비교적 최근인 ES12에 추가되었습니다. Promise.race와 비슷하지만, 빠르게 성공(resolve)한 값을 결과값으로 사용한다는 점에서 차이가 있습니다. 그렇다면 실패한 Promise에 대한 처리가 궁금하실 텐데요. 만약 인자로 받은 모든 Promise가 실패(reject)한 경우 다수의 에러를 하나의 에러로 처리하는 ArrgegateError를 발생합니다. 실패(reject)한 Promise가 있더라도 성공(resolve)한 Promise가 있는 경우 결과값을 반환하기 때문에 유용하게 사용할 수 있습니다.

promise-any.js
// e.g. 성공 케이스 const p1 = new Promise((_, reject) => setTimeout(() => reject("에러 발생"), 100)); const p2 = new Promise(resolve => setTimeout(() => resolve("성공"), 200)); const p3 = new Promise((_, reject) => setTimeout(() => reject("에러 발생"), 300)); Promise.any([p1, p2, p3]).then(console.log); // [console] "성공" // e.g. 실패 케이스 const p1 = new Promise((_, reject) => setTimeout(() => reject("에러 발생"), 100)); const p2 = new Promise((_, reject) => setTimeout(() => reject("에러 발생", 200)); Promise.any([p1, p2]) .then(console.log) .catch(console.error); // [console] AggregateError: All promises were rejected

비교

메서드반환 조건실패 처리사용 예시
Promise.all모든 Promise가 성공하면 결과 반환하나라도 실패하면 reject모든 요청이 성공해야 하는 경우
Promise.allSettled모든 Promise가 완료되면 결과 반환실패한 경우도 결과에 포함성공/실패 여부 상관없이 모든 결과 필요할 때
Promise.race가장 먼저 완료된 Promise를 반환먼저 완료된 Promise가 실패면 즉시 reject가장 빠른 응답을 선택해야 할 때
Promise.any가장 먼저 성공한 Promise를 반환모든 Promise가 실패하면 AggregateError여러 개 중 하나라도 성공하면 되는 경우

이번 글에서는 비동기 함수의 동시성 처리를 위한 메서드들에 대해 알아보았습니다.

각 메서드의 특징에 따라 상황에 맞는 적절한 메서드를 사용할 수 있으면 좋겠습니다.

Related Articles

Github

Linkedin

Instagram