async/await 가 나왔으므로, 이제 프로미스를 안쓴다? 비동기에 await
를 남발하게 되면 도리어 처리가 느려지는 경우도 종종 발생한다. 병렬적으로 Promise
를 동시에 실행시키는 멋진 Promise.All을 직접 만들어보고, 이를 사용해야하는 실제 사례를 함께 알아보자.
비동기 프로그래밍의 산을 함께 넘어보자!
Promise.all
- 여러 Promise Fn을 동시실행(concurrency)
- 모두 성공(fulfilled) 시 시간과 무관하게 순서 보장!
- 하나라도 실패(rejected) 시 바로 catch로!
- Promise.all(iterables).then().catch(...
⇐⇒ new Promise.all([p1, p2]).then(arr => console.log(arr)); // [res1, res2]
promise 메서드
promise.all()
은 모든 프로미스 fulfilled 되길 기다림promise.allSettled()
모든 프로미스가 settled 되길 기다림.promise.race()
넘겨진 모든프로미스들 중 하나라도 settled되길 기다림Promise.any()
넘겨진 프로미스 중 하나라도 fulfilled 되길 기다림import { assertArray } from "../utils/test-utils.js";
const randTime = (val) =>
new Promise((resolve) => {
const delay = Math.random() * 1000;
console.log("randtime :>> ", val, delay);
setTimeout(resolve, delay, val);
});
const promiseAll = (params) => {
}
const vals = [1, 2, 3];
// const randTime = val =>
// new Promise(resolve => setTimeout(resolve, Math.random() * 1000, val));
promiseAll([randTime(1), randTime(2), randTime(3)])
.then((arr) => {
console.table(arr);
assertArray(arr, vals);
})
.catch(console.error);
promiseAll([randTime(11), Promise.reject("RRR"), randTime(33)])
.then((array) => {
console.log("여긴 과연 호출될까?!");
})
.catch((error) => {
console.log("reject!!!!!!>>", error);
});
const assertArray = (arr1, arr2) => {
if (assertEqual(arr1, arr2)) {
console.log(arr1, "==>", "통과");
} else {
console.log(arr1, arr2, "불통");
}
};
비동기 실행 환경을 위해 randTime
함수를 직접 만든 promiseAll
함수의 array 인자로 활용하였다.
then
과 catch
를 활용하기 위해선 promiseAll 함수는 프로미스 객체를 return 해야한다.
const promiseAll = (arr) => {
return new Promise((resolve, reject) => {
for (const item of arr) {
console.log(item);
}
});
};
PromiseAll
이라는 함수를 만들고 arr를 매개 변수로 받는다.for ~ of
문을 사용하여 신나게 돌린다. 일단 console.log를 찍어보자하나하나의 값들은 Promise pending 값으로 찍히는 것을 볼 수 있다. 이 값들을 settled 해주기 위해선 무엇을 해야할까. 내부에서 then
을 돌려야 한다.
const promiseAll = (arr) => {
return new Promise((resolve, reject) => {
for (const item of arr) {
item.then(console.log);👈🏽
}
});
};
item이 then을 만나게해서 그 값을 console.log 찍어보았다.
오호호 각자의 값이 찍히는 것을 볼 수 있다. 이것을 arr에 담아서 resolve 해주면 될까? 일단 해보자.
const promiseAll = (arr) => {
const 리턴배열 = [];👈🏽
return new Promise((resolve, reject) => {
for (const item of arr) {
item.then((res) => {
리턴배열.push(res);👈🏽
if (리턴배열.length === arr.length) {
resolve(리턴배열);
}
});
}
});
};
리턴할 배열 리턴배열
빈 객체를 하나 만들어줬다. 이로서 이 함수는 클로저가 되었다. 두둔
then 메서드로 받은 값을 리턴배열
에 push 해서 때려 넣었다.
resolve 조건은 리턴배열의 길이와 매개변수로 준 arr의 길이가 같을 때 반환하게 했다.
결과 값을 한 번 확인해보자.
오 된다??? 근데? 안된다?
그렇다. 순서보장이 안되고 지맘대로 arr에 들어온 순서대로 때려박는 것이었다.
필수 : - 모두 성공(fulfilled) 시 시간과 무관하게 순서 보장!
그래서 index 값을 써보기로 했다.
const promiseAll = (arr) => {
const 리턴배열 = [];
return new Promise((resolve, reject) => {
for (const item of arr) {
const idx = arr.indexOf(item);👈🏽
item.then((res) => {
리턴배열[idx] = res;👈🏽
if (리턴배열.length === arr.length) {
resolve(리턴배열);
}
});
}
});
};
오오오? 되는데??? 안된다?
if (리턴배열.length === arr.length) {
resolve(리턴배열);
}
empty 값도 index 값을 부여받아 리턴배열의 length로 잡혀서 그냥 resolve 되는 것이다.
이는 제동 장치가 필요해보인다.
const promiseAll = (arr) => {
const 리턴배열 = [];
let pending = 0; 👈🏽
return new Promise((resolve, reject) => {
for (const item of arr) {
const idx = arr.indexOf(item);
item.then((res) => {
리턴배열[idx] = res;
pending += 1;👈🏽
if (pending === arr.length) {👈🏽
resolve(리턴배열);
}
});
}
});
};
pending
이라는 변수에 cnt 기능을 붙여 동작하게했다.
for of 가 한 번 돌 때마다 +=1 해주면서 arr.length가 될 때 resolve를 return 하게 했다.
const promiseAll = (arr) => {
const 리턴배열 = [];
let pending = 0;
return new Promise((resolve, reject) => {
for (const item of arr) {
const idx = arr.indexOf(item);
item.then((res) => {
리턴배열[idx] = res;
pending += 1;
if (pending === arr.length) {
resolve(리턴배열);
}
}).catch(reject);👈🏽
}
});
};
then 뒤에 .catch(reject);
해주면서 에러는 잡아낸다.
.catch(reject); === .catch(error => reject(error)
와 같은 것이다.
Promise.All이 어떤 아이인지 인지 할 수 있었다. 다음은 이를 활용하는 사례에 대해 알아보고자 한다. 분량조절 실패로 다음 편에 !
SSAC 영등포 교육기관에서 풀스택 실무 프로젝트 과정을 수강하고 있다. JS전반을 깊이 있게 배우고 실무에 사용되는 프로젝트를 다룬다. 앞으로 그 과정 중의 내용을 블로그에 다루고자 한다.