Promise 클래스에는 5가지 정적메소드가 있다.
1. Promise.all
만약 여러개의 프로미스를 동시에 실행시키고 모든 프로마스가 준비될 때가지 기다려야한다면?
( 여러개의 URL에 동시에 요청을 보내고 다운로드가 모두 완료된 이후에 필요한 콘텐츠를 실행시키는 경우)
Promise.all을 사용할 수 있다.
let promise = Promise.all([...promises...]);
Promise.all은 요소 전체가 프로미스 배열을 받고, 새로운 프로미스를 반환한다. 배열 안에 프로미스가 모두 실행이 되면 새로운 프로미스가 이행되는데, 배열 안 프라미스의 결괏값을 담은 배열이 새로운 프라미스의 result가 된다.
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(alert);
위의 코드는 3초 뒤(가장 마지막까지 실행되는 프라미스가 끝나는 시점)에 [1,2,3] 이 반환된다.
또한 프로미스 배열의 result값이 담긴 배열은 실행이 된 순서대로 담긴다. (맨 첫번째 프로미스가 가장 늦게 실행되더라도, result 배열에는 가장 먼저 담김)
작업해야 할 데이터가 담긴 배열을 프라미스가 담긴 배열로 매핑하고, 이 배열을 Promise.all로 감싸는 방법은 자주 사용된다.
// Githurb api를 이용해서 유저정보를 가져와야 할 때
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/Violet-Bora-Lee',
'https://api.github.com/users/jeresig'
];
// fetch를 사용해 url을 프라미스로 매핑
let requests = urls.map(url => fetch(url));
// Promise.all은 모든 작업이 이행될 때까지 기다린다.
Promise.all(requests)
.then(responses => responses.forEach(
response => alert(`${response.url}: ${response.status}`)
));
//유저네임만 담긴 배열을 사용해서 유저 정보를 가져오는 예
let names = ['iliakan', 'Violet-Bora-Lee', 'jeresig'];
let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));
Promise.all(requests)
.then(responses => {
// 모든 응답이 성공적으로 이행되었습니다.
for(let response of responses) {
alert(`${response.url}: ${response.status}`); // 모든 url의 응답코드가 200입니다.
}
return responses;
Promise.all에 전달되는 프로미스중 하나라도 거부되면, 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))
]).catch(alert); // Error: 에러 발생!
2초 후에 에버가 발생하면서 .catch문으로 넘어가고 Promise.all 전체가 거부되면서 이때의 거부에러는 Promise.all 전체의 결과가 된다.
❌ 에러가 발생하면 다른 프로미스는 무시된다.
프로미스가 하나라도 거부되면 Promise.all은 즉시 거부되고 배열에 저장된 다른 프로미스들의 결과는 완전히 무시된다.
--> 위의 2개 프로미스 중에 두번째 프로미스에서 거부되었지만, 실행은 3번째 프로미스까지 이행된다.
프라미스에는 '취소’라는 개념이 없어서 Promise.all도 프라미스를 취소하지 않기 때문이다.
2. Promise.allSettled
추가된 지 얼마 안된 문법임
Promise.all은 프라미스가 하나라도 거절되면 전체를 거절한다. 따라서, 프라미스 결과가 모두 필요할 때같이 ‘모 아니면 도’ 일 때 유용하다.
반면, Promise.allSettled는 모든 프라미스가 처리될 때까지 기다린다. 반환되는 배열은 다음과 같은 요소를 갖는다.
- 응답이 성공할 경우 – {status:"fulfilled", value:result}
- 에러가 발생한 경우 – {status:"rejected", reason:error}
fetch를 사용해 여러 사람의 정보를 가져오고 있는데 여러 요청 중 하나가 실패해도 다른 요청 결과는 여전히 필요할 경우가 있다.
이럴 때 Promise.allSettled를 사용할 수 있다.
let urls = [
'https://api.github.com/users/현지',
'https://api.github.com/users/현욱',
'https://no-such-url'
];
Promise.allSettled(urls.map(url => fetch(url)))
.then(results => {
results.forEach((result, index) => {
if (result.status == "fulfilled") {
alert(`${urls[index]}: ${result.value.status}`);
}
if (result.status == "rejected") {
alert(`${urls[index]}: ${result.reason}`);
}
});
});
[
{status: 'fulfilled', value: ...응답...},
{status: 'fulfilled', value: ...응답...},
{status: 'rejected', reason: ...에러 객체...}
]
Promise.allSettled를 사용하면 이처럼 각 프라미스의 상태와 값 또는 에러를 받을 수 있다.
3. Promise.race
Promise.race는 Promise.all과 비슷하지만 가장 먼저 처리되는 프라미스의 결과(혹은 에러)를 반환한다.
let promise = Promise.race(iterable);
Promise.race([
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))
]).then(alert);
//1
위 코드의 결과는 1이다.
첫 번째 프라미스가 가장 빨리 처리상태가 되기 때문에 첫 번째 프라미스의 결과가 result 값이 되는 것이다.
이렇게 Promise.race를 사용하면 '경주(race)의 승자’가 나타난 순간 다른 프라미스의 결과 또는 에러는 무시된다.
4. Promise.resolve & Promise.reject
프라미스 메서드 Promise.resolve와 Promise.reject는 async/await 문법이 생긴 후로 쓸모없어졌기 때문에 근래에는 거의 사용하지 않는다.
** Promise.resolve
Promise.resolve(value)는 결괏값이 value인 이행 상태 프라미스를 생성한다. 또한 함수가 프라미스를 반환하도록 해야 할 때 사용할 수 있다.
아래 함수 loadCached는 인수로 받은 URL을 대상으로 fetch를 호출하고, 그 결과를 기억(cache)한다.
나중에 동일한 URL을 대상으로 fetch를 호출하면 캐시에서 호출 결과를 즉시 가져오는데, 이때 Promise.resolve를 사용해 캐시 된 내용을 프라미스로 만들어 반환 값이 항상 프라미스가 되게 한다.
let cache = new Map();
function loadCached(url) {
if (cache.has(url)) {
return Promise.resolve(cache.get(url)); // (*)
}
return fetch(url)
.then(response => response.text())
.then(text => {
cache.set(url,text);
return text;
});
}
loadCached를 호출하면 이행상태의 프라미스가 반환된다는 것이 보장되기 때문에 loadCached(url).then(…)을 사용할 수 있다.
-> .then은 프라미스가 성공했을 때에만 사용할 수 있기 때문에 반드시 성공한 프라미스를 반환하는 loadCached에서 .then을 사용할 수 있다는 뜻이다.
** Promise.reject
Promise.reject(error)는 결괏값이 error인 거부 상태 프라미스를 생성한다.
아래 코드와 동일한 일을 수행한다.
let promise = new Promise((resolve, reject) => reject(error));
//실무에서 쓰일 일은 거의 없다고한다.
'Front-end > JavaScript' 카테고리의 다른 글
[JavaScript] Promise 프라미스화 (2) | 2022.11.28 |
---|---|
[JavaScript]프라미스와 에러 핸들링 (0) | 2022.11.14 |
[JavaScript]프라미스 체이닝 (0) | 2022.11.14 |
[JavaScript] setTimeout과 setInterval을 이용한 호출 스케줄링 (0) | 2022.11.07 |
[JavaScript] new Function 문법 (0) | 2022.11.07 |