Iteration도 알아봤고 하니 Promise.prototype.all에 대해서 알아보자.
MDN은 Promise.all 메소드에 대해 이렇게 설명하고 있다.
Promise.all() 메서드는 순회 가능한 객체에 주어진 모든 프로미스가 이행한 후, 혹은 프로미스가 주어지지 않았을 때 이행하는 Promise를 반환합니다. 주어진 프로미스 중 하나가 거부하는 경우, 첫 번째로 거절한 프로미스의 이유를 사용해 자신도 거부합니다.
여기서 순회 가능한 객체란 이터러블 객체를 의미한다.
아래의 코드를 보자.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
Promise.all은 코드 실행 100ms뒤에 PromiseResult가 각 프로미스의 PromiseResult인 프로미스를 반환했다.
Promise.all은 인자로 받은 이터러블 객체(여기서는 Array) 내부의 Promise들을 순회하며 모든 Promise가 이행될 때 까지 기다린 뒤에 결괏값들을 배열에 담는다. 그 뒤 결괏값이 담긴 배열을 PromiseResult로 지닌 Promise를 생성하여 이행한다. 그래서 Promise.all.then 체이닝이 가능하고, then이 [3, 42, "foo"]를 출력한 것이다.
또한 Promise.all이 반환하는 Promise의 PromiseResult에 Promise들의 결괏값이 담기는 순서는 Promise.all의 인자 배열에 담겨 있는 순서이다.
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3
]).then(console.log);
Promise가 이행되는 순서와는 관계가 없다.
또한 Promise.all로 전달되는 Promise중 하나라도 거부(rejected)된다면 그 즉시 Promise.all은 거부된 Promise를 반환한다.
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("error")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(console.log);
다른 Promise의 이행여부와 상관없이 Promise.all의 Promise가 거부된다.
그리고 만일 Promise.all에 Promise가 아닌 값이 전달되면, 그 값은 그대로 Promise.all의 PromiseResult에 등록된다.
Promise.all([
new Promise((resolve, reject) => resolve("Promise")),
1,
"Isnt Promise",
]).then(console.log);
Promise.prototype에는 다른 흥미로운 메소드들도 있다.
Promise.allSettled는 Promise.all과 비슷하지만, Promise를 반환하지 않고 각 Promise의 Status와 Result를 담은 배열을 반환한다.
const promise1 = Promise.resolve(3);
const promise2 = Promise.reject(new Error("yey error"));
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.allSettled([promise1, promise2, promise3]).then(console.log);
또한 전달받은 Promise중 하나라도 거부되면 거부된 Promise를 반환하는 Promise.all과 달리, 모든 반환된 Promise를 반환한다.
Promise.race는 전달받은 Promise중 가장 빨리 실행되는 Promise만을 반환한다.
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 300, '1');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(reject, 200, new Error("error"));
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, '3');
});
Promise.race([promise1, promise2, promise3]).then(console.log).catch(console.log);
rejected Promise가 제일 빨리 반환된다면 어떻게 될까?
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 300, '1');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(reject, 100, new Error("error"));
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 200, '3');
});
Promise.race([promise1, promise2, promise3]).then(console.log).catch(console.log);
물론 rejected Promise를 반환한다. Promise.race는 이행 여부와는 관계없이 가장 빨리 반환되는 Promise를 반환한다.
불과 이틀 전만해도 미지의 공포가 도사리고 있던 Promise와 Iteration Protocol은 재미있는 도구가 되었다. 23년 평생 재미있었던 공부라고는 게임 공부와 일본어 공부 뿐이었는데, 프로그래밍 공부가 지금 제일 재밌다. 게임도 미뤄놓고 모르는 개념들을 알아 보며 포스팅을 하고 있다.
다음 포스팅은 Generator에 대해 알아보는 시간을 가질 것이다. Generator는 어떤 개념일지 상당히 기대된다.