Language/JavaScript

프라미스 API

nayoon030303 2021. 7. 16. 01:08

프라미스 API

Promise 클래스에는 5가지 정적 메서드가 있습니다. 이번 시간에는 5가지의 메서드를 알아보도록 하겠습니다. 

 

 

Promise.all

여러 개의 프라미스들을 동시에 실행시킵니다. 모든 프라미스가 준비가 될 때까지 기다립니다. 

 

문법

let promise = Promise.all([..promises...]);

 

예시

let promise = Promise.all([
    new Promise(resolve=>setTimeout(()=>resolve(1),3000)),
    new Promise(resolve=>setTimeout(()=>resolve(2),2000)),
    new Promise(resolve=>setTimeout(()=>resolve(3),1000)),
]).then(response=>{
    console.log(response);
});

결과: 3초 후 [1,2,3]

 

위의 코드를 보면 3번째 프라미스가 1초 후 완료, 2번째 프라미스가 2초 후 완료, 3번째 프라미스가 3초 후 완료됩니다. 

실행 순서와 상관없이 첫번쨰 프라미스 결과는 첫 번째 배열에 저장됩니다. 

 

작업해야 할 데이터가 담긴 배열을 프라미스 배열로 매핑하고, 이 배열을 Promise.all로 감싸는 트릭은 자주 사용됩니다. 

 

URL이 담긴 배열을 fetch를 써서 처리하는 예시

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  '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}`)
));

 

 

Promise.all에 전달되는 프라미스들 중 하나라도 거부되면, Promise.all이 반환하는 프라미스는 에러와 함께 바로 거부됩니다. 

Error가 날때 
Promise.all([
    new Promise((resolve,reject)=>setTimeout(()=>resolve(1),1000)),
    new Promise((resolve,reject)=>setTimeout(()=>reject(new Error('에러발생')),3000)),
    new Promise((resolve,reject)=>setTimeout(()=>resolve(1),2000))
]).then(requests=>{
    requests.forEach(re=>console.log(req));
})

결과: Error: 에러발생

 

프라미스가 하나라도 거부되면 Promise.all은 즉시 거부되고 배열에 저장된 다른 프라미스의 결과를 완전히 잊어버립니다. 이미 이행된 프라미스 결과들도 무시됩니다.

 

 

+이터러블 객체가 아닌 "일반" 값도 Promise.all(iterable)에 넘길 수 있습니다. 

Promise.all(..)은 대부분 프라미스가 요소인 이터러블 객체를 받습니다. 그런데 프라미스가 아닌 객체가 배열을 구성하면 그 요소가 '그대로' 결과 배열로 전달됩니다.

Promise.all([
	new Promise(resolve=>setTimeout(()=>resolve(1),1000)),
    3,
    2
]).then(alert);

결과: 1,3,2

 

Promise.allSettled

Promise.all은 프라미스가 하나라도 거절되면 전체를 거절합니다. 하지만 Promise.allSettled는 모든 프라미스가 처리될 때까지 기다립니다. 반환되는 배열은 다음과 같은 요소를 갖습니다. 즉 하나의 프로미스가 거절돼도 전체를 거절하지 않습니다. 

  • 응답이 성공한 경우: {status:"fulfilled", value: result}
  • 에러가 발생한 경우: {status:"rejected", reason: error"}
let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  '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}`);
      }
    });
  });

위의 코드에서 (*)로 표시한 줄의 results는 다음과 같습니다. 

[
  {status: 'fulfilled', value: ...응답...},
  {status: 'fulfilled', value: ...응답...},
  {status: 'rejected', reason: ...에러 객체...}
]

 

Promise.allSettled를 사용하면 각 프라미스의 상태와 값 또는 에러를 받을 수 있습니다. 

 

 

Promise.race

Promise.race는 Promise.all과 비슷합니다. 하지만 가장 먼저 처리된 프라미스의 결과(또는 에러)를 반환합니다. 

 

예시

Promise.race([
    new Promise(resolve=>setTimeout(()=>resolve(1),3000)),
    new Promise(resolve=>setTimeout(()=>reject(new Error("에러발생")),2000)),
    new Promise(resolve=>setTimeout(()=>resolve(3),1000)),
]).then(result=>console.log(result));

결과: 1

 

첫 번째 프라미스가 가장 빨리 결과를 반환하기 때문에 result의 값이 됩니다. 두 번째 프라미스에서 에러가 발생해도 이미 result값이 나타난 순간에는 다른 프라미스의 결과 또는 에러는 모두 무시됩니다. 

 

Promise.resolve/reject

프라미스 메서드 Promise.resolve와 Promise.reject는 async/await 문법이 생긴 이후로 쓸모가 없어져서 근래에는 거의 사용하지 않는다고 합니다. 

하지만 저는 아직 async/await에 대해서 자세히 모르고 Promise의 5가지 함수들을 알기 위해 작성해봅니다. 

 

Promise.resolve

Promise.resolve(value)는 결괏값이 value인 이행 상태 프라미스를 생성합니다. 

아래의 코드와 동일한 역할을 수행합니다. 

let promise = new Promise(resolve=>resolve(value));

 Promise.resolve는 호환성을 위해 함수가 프라미스를 반환하도록 해야 할 때 사용할 수 있습니다. 

 

Promise.reject

Promise.reject(error)는 결괏값이 error인 거부 상태 프라미스를 생성합니다. 

아래 코드와 동일한 역할을 수행합니다. 

let promise = new Promise((resolve,reject)=>reject(error));

실무에서 이 메서드를 쓸 일은 거의 없다고 합니다.