하얀 코딩

[JavaScript - 23] Promise API 본문

JavaScript

[JavaScript - 23] Promise API

whitecoding 2023. 1. 26. 22:03

Promise.prototype.all()

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

Promise.all()은 여러 개의 비동기 작업을 동시에 처리하고 싶을때 사용합니다. 인자로는 배열을 받습니다.

해당 배열에 있는 모든 Promise에서 executor 내 작성했던 코드들이 정상적으로 처리가 되었다면

결과를 배열에 저장해 새로운 Promise를 반환 해줍니다.

 

앞서 배운 Promise chaining을 사용했을 경우는 코드들이 순차적으로 동작되기 때문에 총 6초의 시간이 걸리게 됩니다.

또한, 같은 코드가 중복되는 현상도 발생하게 됩니다.

// Promise chaining을 사용

const promiseOne = () => new Promise((resolve, reject) => setTimeout(() => resolve('1초'), 1000));
const promiseTwo = () => new Promise((resolve, reject) => setTimeout(() => resolve('2초'), 2000));
const promiseThree = () => new Promise((resolve, reject) => setTimeout(() => resolve('3초'), 3000));

const result = [];
promiseOne()
  .then(value => {
    result.push(value);
    return promiseTwo();
  })
  .then(value => {
    result.push(value);
    return promiseThree();
  })
  .then(value => {
    result.push(value);
   console.log(result);  
   // ['1초', '2초', '3초']
  })

이러한 문제들을 Promise.all()을 통해 해결할 수 있습니다. Promise.all()은 비동기 작업들을 동시에 처리합니다.

따라서 3초 안에 모든 작업이 종료됩니다. 또한 Promise chaining로 작성한 코드보다 간결해진 것을 확인할 수 있습니다.

// Promise.all 사용

Promise.all([promiseOne(), promiseTwo(), promiseThree()])
  .then((value) => console.log(value))
  .catch((err) => console.log(err));

 

2초 후 두 번째 프라미스가 거부되면서 Promise.all 전체가 거부되고, .catch가 실행됩니다. 

거부 에러는 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: 에러 발생!

Promise.allSettled() 

Promise.all은 프라미스가 하나라도 거절되면 전체를 거절합니다. 

반면, Promise.allSettled는 모든 프라미스가 처리될 때까지 기다립니다. 반환되는 배열은 다음과 같은 요소를 갖습니다.

 

응답이 성공할 경우 – {status:"fulfilled", value:result}
에러가 발생한 경우 – {status:"rejected", reason:error}

 

fetch를 사용해 여러 사람의 정보를 가져오고 있다고 해봅시다. 

여러 요청 중 하나가 실패해도 다른 요청 결과는 여전히 필요합니다.
이럴 때 Promise.allSettled를 사용할 수 있습니다.

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/Violet-Bora-Lee',
  '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.prototype.race() 

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

let promise = Promise.all([...promises...]);
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.race를 사용하면 '경주(race)의 승자’가 나타난 순간 다른 프라미스의 결과 또는 에러는 무시됩니다.