Promise
클래스는 5가지 정적 메서드를 가지고 있다
여러 개의 프라미스를 동시에 실행시키고 모든 프라미스가 준비될 때까지 기다린다고 가정할 때
복수의 URL에 동시에 요청을 보내고 다운로드가 모두 완료된 후에 콘텐츠를 처리할 때 이런 상황이 발생한다
let promise = Promise.all([...promises...]);
Promise.all
은 요소 전체가 프라미스인 배열(iterable 객체)을 받고 새로운 프라미스를 반환한다
배열 안 프라미스가 모두 처리되면 새로운 프라미스가 이행되는데, 배열 안 프라미스의 결괏값을 담은 배열이 새로운 프라미스의 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);
Promise.all
은 3초 후에 처리되고, 반환되는 프라미스의 result
는 배열 [1, 2, 3]
이 된다
배열 result
의 요소 순서는 Promise.all
에 전달되는 프라미스 순서와 상응한다
Promise.all
의 첫 번째 프라미스는 가장 늦게 이행되더라도 처리 결과는 배열의 첫 번째 요소에 저장된다
let urls = [
'https://api.github.com/users/jinju',
'https://api.github.com/users/baek',
'https://api.github.com/users/pearl'
];
// fetch를 사용해 url을 프라미스로 매핑
let requests = urls.map(url => fetch(url));
// Promise.all은 모든 작업이 이행될 때까지 기다림
Promise.all(requests)
.then(responses => responses.forEach(
response => alert(`${response.url}: ${response.status}`)
));
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
Promise.all
에 전달되는 프라미스 중 하나라도 거부되면, Promise.all
이 반환하는 프라미스는 에러와 함께 바로 거부된다
2초 후 두 번째 프라미스가 거부되면서 Promise.all
전체가 거부되고, .catch
가 실행된다
거부 에러는 Promise.all
전체의 결과가 된다
프라미스가 하나라도 거부되면 Promise.all
은 즉시 거부되고 배열에 저장된 다른 프라미스의 결과는 사라져버리고 이행된 프라미스의 결과도 무시된다
fetch
를 사용해 호출 여러 개를 만들면, 그 중 하나가 실패하더라도 호출은 계속 일어난다
그렇더라도 Promise.all
은 다른 호출을 더는 신경쓰지 않는다
프라미스가 처리되긴 하나 그 결과는 무시된다
프라미스에는 취소 개념이 없어서 Promise.all
도 프라미스를 취소하지 않습니다
AbortController
를 사용하면 프라미스 취소가 가능하긴 하지만 프라미스 API는 아니다
Promise.all(...)
은 대개 프라미스가 요소인 iterable 객체(대부분 배열)를 받는다
그런데 프라미스가 아닌 객체가 배열을 구성하면, 요소가 그래도 결과 배열로 전달된다
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000)
}),
2,
3
]).then(alert); // 1, 2, 3
그래서 이미 결과를 알고 있는 값은 Promise.all
로 그냥 전달하면 된다
구식 브라우저는 폴리필이 필요하다
프라미스가 하나라도 거절되면 전체를 거절하는 Promise.all
에 반해 Promise.allSettled
는 모든 프라미스가 처리될 때 까지 기다린다
반환되는 배열은 다음과 같은 요소를 가진다
{status: "fulfilled", value: result}
{status: "rejected", reason: error}
let urls = [
'https://api.github.com/users/jinju',
'https://api.github.com/users/baek',
'https://no-such-url'
];
Promise.allSettled(urls.map(url => fetch(url)))
.then(results => { // (*)
results.forEach((result, num) => {
if (result.status == "fulfilled") {
alert(`${urls[num]}: ${result.value.status}`);
}
if (result.status == "rejected") {
alert(`${urls[num]}: ${result.reason}`);
}
});
});
fetch
를 사용해 여러 사람의 정보를 가져올 때 여러 요청 중 하나가 실패해도 다른 요청 결과는 여전히 있어야 할 때 Promise.allSettled
가 사용된다
[
{status: 'fulfilled', value: ...응답...},
{status: 'fulfilled', value: ...응답...},
{status: 'rejected', reason: ...에러 객체...}
]
Promise.allSettled
를 사용하면 위 처럼 각 프라미스의 상태와 값 또는 에러
를 받을 수 있다
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
첫 번 째 프라미스가 가장 빨리 처리상태가 되기 때문에 첫 번째 프라미스의 결과가 result값이 된다
프라미스 메서드 Promise.resolve
와 Promise.reject
는 async/await
이 생긴 이후로 쓸모 없어졌기 때문에 잘 사용되지 않는다
Promise
클래스에는 5가지 정적 메서드가 있다
Promise.all(promises)
: 모든 프라미스가 이행될 때 까지 기다렸다가 그 결괏값을 담은 배열을 반환한다. 주어진 프라미스 중 하나라도 실패하면 Promise.all
은 거부되고, 나머지 프라미스의 결과는 무시된다Promise.allSettled(promises)
: 최근에 추가된 메서드로 모든 프라미스가 처리될 때 까지 기다렸다가 그 결과(객체)를 담은 배열을 반환한다status
: "fulfilled"
또는 "rejected"
value
또는 reason
Promise.race(promises)
: 가장 먼저 처리된 프라미스의 결과 또는 에러를 담은 프라미스를 반환한다Promise.resolve(value)
: 주어진 값을 사용해 이행 상태의 프라미스를 만든다 Promise.reject(error)
: 주어진 에러를 사용해 거부 상태의 프라미스를 만든다