도리쓰에러쓰

[JavaScript] 자바스크립트에서의 Promise 본문

JavaScript/JS

[JavaScript] 자바스크립트에서의 Promise

강도리 2022. 8. 15. 03:09

자바스크립트의 Promise를 알아보기 전에 동기와 비동기의 차이에 대해 알아보자.


1. 동기(Synchronous)와 비동기(Asynchronous)

1️⃣ 동기(Synchronous)

- 1번부터 4번까지의 명령이 있을 때 순차적으로 명령이 진행

- 앞의 명령이 끝날 때까지 뒤의 명령은 먼저 실행되지 않는다.

 

2️⃣ 비동기(Asynchronous)

- 명령의 순서와 상관없이 동시에 명령 실행


2. Promise

자바스크립트는 비동기 처리를 위해 콜백 함수를 사용한다.

하지만 콜백 중첩, 즉 콜벡 헬로 인해 코드의 가독성이 떨어지고,

비동기 처리 중 발생한 에러의 처리가 곤란한 등의 문제가 발생할 수 있다.

async(1, () => {
  async(2, () => {
    async(3, () => {
      async(4, () => {
        async(5, () => {
          console.log('콜백의 늪');
        });
      });
    });
  });
});

 

Promise는 비동기 연산이 종료된 이후에 결과 값과 실패 사유를 처리하기 위한 것이고,

이것을 이용하면 비동기 메서드를 마치 동기 메서드처럼 값을 반환할 수 있다.

 

Promise는 다음 중 하나의 상태를 가진다.

1️⃣ 대기(Pending): 이행하지도, 거부하지도 않은 초기 상태

2️⃣ 이행(Fulfilled): 연산이 성공적으로 완료

3️⃣ 거부(Rejected): 연산 실패

 

아래 예제를 보자.

const exFunc = (param) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (param) resolve('성공');
      else reject(new Error('실패'));
    }, 3000);
  });
};

위 예제는 Promise 객체를 리턴하는 예제이다.

Promise는 resolve와 reject를 파라메터로 받고 있는데,

일단 new Promise로 Promise가 생성되는 직후부터 resolve나 reject가 호출되기 전까지의 순간을 대기(Pending) 상태라고 볼 수 있다.

이 후 비동기 작업이 성공하면 resolve 함수를 호출하고 실패하면 reject 함수를 호출한다.

💡 then()

then() 메서드는 Promise를 리턴하고 두개의 콜백 함수를 인수로 받는다.

하나는 Promise가 이행했을 때, 다른 하나는 거부했을 때를 위한 콜백 함수이다.

p.then((value) => {
  // 이행
}, (reason) => {
  // 거부
});

💡 catch()

catch() 메서드는 거부했을 때 즉, reject인 경우에만 실행된다.

p.then((value) => {
  // 이행
}.catch((reason) => {
  // 거부
});

then() 메서드로 이행과 거부 상태가 발생했을 때의 처리를 해줄 순 있지만,

코드의 가독성을 위해 then() 메서드는 이행했을 때

catch() 메서드는 거부했을 때의 코드를 작성하는 것이 좋다.

💡 finally()

finally() 메서드는 이행과 거부 상관 없이 처리가 완료되면 실행된다.

p.then((value) => {
  // 이행
}.catch((reason) => {
  // 거부
}.finally(() => {
  console.log('성공');
});

💡 Promise.all()

- 사용법: Promise.all(iterable);

📌 iterable: array처럼 순회 가능한 객체

 

Promise.all() 메서드는 순회 가능한 객체에 주어진 모든 프로미스가 이행한 후 혹은

프로미스가 주어지지 않았을 때 이행하는 Promise를 반환한다.

아래 예제를 확인해보자.

const p1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('첫번째'), 1000);
  });
};

const p2 = (msg) => {
  console.log(msg);
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('두번째'), 3000);
  });
};

const p3 = (msg) => {
  console.log(msg);
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('세번째'), 2000);
  });
};

Promise.all([p1, p2, p3])
.then(values => {
  console.log(values);
})
.catch(error => {
  console.log(error);
});

// 출력: ['첫번째', '두번째', '세번째']

위 예제는 p1, p2, p3 프로미스가 모든 프로미스가 이행되고

그 작업이 완료하여 then() 메소드를 통해 배열로 리턴한 예제이다.

3개 모두 문제 없이 이행되었기 때문에 배열로 리턴하였지만, 만약 하나라도 실패한 것이 있다면 어떻게 될까?

 

아래 예제를 보자.

const p1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('첫번째'), 1000);
  });
};

const p2 = (msg) => {
  console.log(msg);
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('두번째'), 3000);
  });
};

const p3 = (msg) => {
  console.log(msg);
  return new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('거부')), 2000);
  });
};

Promise.all([p1, p2, p3])
.then(values => {
  console.log(values);
})
.catch(error => {
  console.log(error.message);
});

// 출력: '거부'

위 예제처럼 어느 하나라도 실패했다고 뜨면 어떤 데이터도 얻지 못한다.

그래서 Promise.all()을 사용할 때는 다 보여주거나 혹은 모든 정보를 다 보여주지 않거나 할 때 사용하는 것이 좋다.

💡 Promise.race()

Promise.race()는 Promise.all()과 사용법이 같다.

Promise 객체를 반환하며 iterable 안에 있는 프로미스 중 가장 먼저 완료된 것의 결과값을 그대로 이행하거나 거부한다.

const p1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, 500, '첫번째');
  });
};

const p2 = (msg) => {
  console.log(msg);
  return new Promise((resolve, reject) => {
    setTimeout(resolve, 100, '두번째');
  });
};

Promise.race([p1, p2])
.then(value => {
  console.log(value);
});

// 출력: '두번째'

위 예제는 p1, p2 중 p2가 먼저 완료되어 '두번째'가 출력된 예제이다.

'두번째'가 출력되고 '첫번째'가 reject를 예상하고 있었는데 이미 가장 먼저 완료된 p2가 있어서 무시된 것이다.

💡 Promise.allSettled()

Promise.allSettled()도 마찬가지로 Promise.all()과 사용법이 같다.

Promise.all() 메서드와 반대로 하나가 실패하더라도 모든 Promise의 결과를 받을 수 있다.

const p1 = Promise.resolve('첫번째');
const p2 = new Promise((resolve, reject) => setTimeout(reject, 100, '두번째'));
const p3 = Promise.resolve('세번째');

Promise.allSettled([p1, p2, p3])
.then(results => {
  results.forEach((result) => console.log(result.value));
})
.catch(error => {
  console.log(error.message);
});

// 출력
// '첫번째'
// undefined
// '세번째'

에러가 난 p2만 undefined라고 출력되고 p1과 p3는 정상적으로 콘솔에 출력된 것을 확인할 수 있다.

Comments