Promise, async/await

코이그·2023년 7월 26일

Promise

  • ES6에서 도입된 비동기 처리에 사용되는 객체.
  • 전통적인 콜백 패턴이 가진 단점(콜백지옥, 오류처리)을 보완하며 비동기 처리 시점을 명확하게 표현할 수 있다는 장점이 있음.

Promise의 4가지 상태

  1. pending: 비동기 처리가 아직 수행되지 않은 상태 (resolve/reject가 아직 호출되지 않은 상태)
  2. fulfilled: 비동기 처리가 수행된 상태 (성공) (resolve가 호출된 상태)
  3. rejected: 비동기 처리가 수행된 상태 (실패) (reject가 호출된 상태)
  4. settled: fulfilled | rejected

사용법

// 예시)
// resolve와 reject를 둘 다 호출하는 Promise 예시
1  function fetchData(condition) {
2    return new Promise((resolve, reject) => {
3      setTimeout(() => {
4        if (condition) {
5          const data = "Fetched data from server";
6          // 성공적으로 완료될 때 resolve를 호출하여 결과 값을 전달
7          resolve(data);
8        } else {
9          // 실패할 때 reject를 호출하여 에러를 전달
10         reject(new Error("Failed to fetch data from server"));
11       }
12     }, 2000);
13   });
14 }
15
16 // Promise 사용 예시
17 fetchData(true)
18   .then((result) => {
19     console.log(result); // "Fetched data from server" (2초 후에 출력)
20   })
21   .catch((error) => {
22     console.error(error); // 실패할 경우, Error 객체 출력 (조건에 따라 출력 여부 달라짐)
23   });
  • 성공 시:
    • 2초 뒤에 resolve(data)가 호출되는데, 이 data는 18번째 줄에서 then에서 받을 수 있다.
    • 결과적으로 2초 뒤에 "Fetched data from server"가 출력된다.
  • 실패 시:
    • 2초 뒤에 reject(new Error())가 호출된다. 이 에러객체는 21번째 줄의 catch에서 받을 수 있다.
    • 결과적으로 "Failed to fetch data from server" 오류 객체가 출력된다.

then, catch

then

  • then 메소드는 두 개의 콜백 함수를 인자로 전달 받음: 1) 성공 시 호출 함수, 2) 실패 시 호출 함수.
promiseAjax(wrongUrl)
  .then(res => console.log(res), err => console.error(err)); // Error: 404
  • Promise를 반환한다.
  • 하지만 then 메소드에 두 번째 콜백 함수를 전달하면 첫 번째 콜백 함수에서 발생한 에러를 캐치하지도 못하고 코드가 복잡해지기 때문에 catch를 사용하는게 훨씬 낫다.

catch

  • catch 메소드는 비동기 처리에서 혹은 then 메소드에서 에러가 발생하면 호출된다.
  • Promise를 반환한다.
promiseAjax(wrongUrl)
  .then(res => console.log(res))
  .catch(err => console.error(err)); // Error: 404
  • 위처럼 catch 메소드를 호출하면 내부적으로 then(undefined, onRejected)을 호출한다.
promiseAjax(wrongUrl)
  .then(res => console.log(res))
  .then(undefined, err => console.error(err)); // Error: 404

프로미스 체이닝

  • 여러 개의 비동기 작업을 순차적으로 연결하여 처리.
  • then 메소드를 통해 연결되어 결과 전달 혹은 다음 비동기 작업 실행.
function fetchData(condition) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (condition) {
        const data = "Fetched data from server";
        resolve(data);
      } else {
        reject(new Error("Failed to fetch data from server"));
      }
    }, 2000);
  });
}

fetchData(true)
  .then((result1) => {
    console.log(result1);
    return fetchData(false);
  })
  .then((result2) => {
    console.log(result2);
  })
  .catch((error) => {
    console.error(error);
  });
  • then이 연속으로 두 번 쓰이는데, 첫 번째 then 메소드에서 fetchData(false)가 반환되었기 때문에 두 번째 then 메소드는 실행되지 않음.

프로미스의 정적 메소드

Promise.resolve / Promise.reject

  • 존재하는 값을 Promise로 래핑하기 위해 사용.
const resolvedPromise = Promise.resolve([1, 2, 3]);
resolvedPromise.then(console.log); // [ 1, 2, 3 ]

const rejectedPromise = Promise.reject(new Error('Error!'));
rejectedPromise.catch(console.log); // Error: Error!

// above are same as below:

const resolvedPromise = new Promise(resolve => resolve([1, 2, 3]));
resolvedPromise.then(console.log); // [ 1, 2, 3 ]

const rejectedPromise = new Promise((resolve, reject) => reject(new Error('Error!')));
rejectedPromise.catch(console.log); // Error: Error!

Promise.all

  • 프로미스가 담겨 있는 배열 등의 이터러블을 인자로 전달 받은 뒤 모든 프로미스를 병렬로 처리한 후 그 처리 결과를 resolve하는 새로운 프로미스를 반환.
  • 모든 프로미스의 처리가 종료될 때까지 기다린 후 모든 처리 결과를 resolve/reject 한다.
  • resolve: 각각의 프로미스가 resolve한 처리 결과를 배열에 담아 resolve하는 새로운 프로미스를 반환한다. 처리 순서가 보장된다 (첫 번째 프로미스가 가장 나중에 처리되더라도 배열에는 첫 번째 요소로 담긴다)
  • reject: 가장 먼저 실패한 프로미스가 reject한 에러를 reject하는 새로운 프로미스가 반환된다.
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(console.log) // [ 1, 2, 3 ]
  .catch(console.log);

첫 번째 프로미스: 3초 후에 1을 resolve하여 처리 결과 반환
두 번째 프로미스: 2초 후에 2를 resolve하여 처리 결과 반환
세 번째 프로미스: 1초 후에 3을 resolve하여 처리 결과 반환

즉, 3초 후에 [1, 2, 3]이 출력된다.

Promise.race

  • Promise.all과 비슷하지만 가장 먼저 처리된 프로미스가 resolve한 처리 결과를 resolve하는 새로운 프로미스를 반환한다.
  • reject는 Promise.all과 동일하다.

async/await

  • ES8에서 도입된 비동기 처리를 구현할 수 있는 키워드.
  • async 키워드를 함수의 앞에 붙이면 해당 함수는 항상 Promise를 반환.
  • await 키워드를 사용하여 Promise가 처리될 때까지 기다리는 것처럼 작성 가능.
function fetchData(condition) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (condition) {
        const data = "Fetched data from server";
        resolve(data);
      }
      else {
        reject(new Error("Something went wrong"));
      }
    }, 2000);
  });
}

async function fetchDataAsync() {
  try {
    const result = await fetchData(false);
    console.log(result);
  } catch (error) {
    console.error(error);
  }
}

fetchDataAsync();
  • await: fetchData 함수가 Promise가 처리될 때까지 기다렸다가 결과 값을 반환하게 함
profile
COYG🔴⚪

0개의 댓글