[JavaScript] Promise

olwooz·2023년 1월 24일
0

JavaScript

목록 보기
4/5

Promise

JavaScript의 콜백 함수는 비동기 처리를 위한 하나의 패턴으로, 아래와 같은 단점이 존재한다.

  • 콜백 헬로 인해 가독성↓
  • 비동기 처리 중 발생한 에러 처리 곤란
  • 여러 개의 비동기 코드를 한 번에 처리하는 데에 한계 존재

Promise는 이를 보완하기 위해 ES6에서 도입된 또 다른 비동기 처리 패턴이다. Promise의 장점은 다음과 같다.

  • 전통적인 콜백 패턴 단점 보완
  • 비동기 처리 시점 명확하게 표현

프로미스의 생성

Promise 생성자 함수를 new 연산자화 함께 호출하면 Promise 객체가 생성된다.
resolve, reject 함수를 인수로 전달할 수 있다.

  • 비동기 처리 성공 → 처리 결과를 resolve 함수에 인수로 전달해 호출
  • 비동기 처리 실패 → 처리 결과를 reject 함수에 인수로 전달해 호출

Promise는 비동기 처리 상태 정보를 가진다.

  • pending - 비동기 처리가 수행되지 않은 상태
  • fulfilled - 성공
  • rejected - 실패
const promise = new Promise((resolve, reject) => {
	if (/* success */) {
		resolve('result');    
	} else { /* fail */
		reject('reason for failure');
    }
});

프로미스의 후속 처리 메서드

프로미스의 비동기 처리 상태가 변화하면 후속 처리 메서드에 인수로 전달한 콜백 함수가 선택적으로 호출된다.

Promise.prototype.then

두 개의 콜백 함수를 인수로 전달받는다.

  1. 프로미스가 fulfilled 상태가 되면 호출
  2. rejected 상태가 되면 호출

Promise를 반환한다.

※ 에러를 then의 두 번째 인수를 사용해 처리하면 가독성이 좋지 않을 뿐더러 then의 첫 번째 인수인 콜백 함수에서 발생한 에러는 처리할 수 없다. 따라서 에러 처리는 아래 catch를 사용해서 하는 것을 권장한다.


Promise.prototype.catch

한 개의 콜백 함수를 인수로 전달받는다.
rejected 상태인 경우만 호출된다.
Promise를 반환한다.


Promise.prototype.finally

한 개의 콜백 함수를 인수로 전달받는다.
성공, 실패 여부 관계 없이 무조건 한 번 호출된다.
Promise를 반환한다.


then, catch, finally는 언제나 Promise를 반환하기 때문에 연속적으로 호출이 가능하고,
이를 프로미스 체이닝 이라고 한다.

promise()
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log('finished'));

프로미스의 정적 메서드

Promise는 5가지 정적 메서드를 제공한다.

Promise.resolve / Promise.reject

이미 존재하는 값으로 Promise를 생성하기 위해 사용된다.

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

Promise.all

여러 개의 비동기 코드를 병렬적으로 처리할 때 사용한다.

const reqData1 = () => new Promise(res => setTimeout(() => res(1), 1000));
const reqData2 = () => new Promise(res => setTimeout(() => res(2), 1000));
const reqData3 = () => new Promise(res => setTimeout(() => res(3), 1000));

Promise.all([reqData1(), reqData2(), reqData3()]
	.then(console.log) // [1, 2, 3] => 약 1초 소요

모든 Promisefulfilled가 되면 종료된다.
처리 순서를 보장한다.
하나라도 rejected되면 나머지를 기다리지 않고 즉시 종료된다.
전달받은 이터러블의 요소가 Promise가 아닌 경우 Promise.resolve를 통해 래핑된다.


Promise.race

Promise.all과 동일하게 Promise를 요소로 갖는 이터러블을 인수로 전달받는다.
Promise.all과 다른 점은 모든 Promise를 기다리는 대신 가장 먼저 fulfilled된 프로미스 처리 결과를 resolve하는 새로운 Promise를 반환한다.
하나라도 rejected되면 에러를 reject하는 새로운 Promise를 즉시 반환한다.

const reqData1 = () => new Promise(res => setTimeout(() => res(1), 3000));
const reqData2 = () => new Promise(res => setTimeout(() => res(2), 2000));
const reqData3 = () => new Promise(res => setTimeout(() => res(3), 1000));

Promise.race([reqData1(), reqData2(), reqData3()]
	.then(console.log) // 3

Promise.allSettled

Promise를 요소로 갖는 이터러블을 인수로 전달받는다.
모두 settled상태 (fulfilled/rejected)가 되면 결과를 배열로 반환한다.

const reqData1 = () => new Promise(res => setTimeout(() => res(1), 2000));
const reqData2 = () => new Promise((_, reject) => setTimeout(() => reject(new Error('error')), 1000));

Promise.allSettled([reqData1(), reqData2()]
	.then(console.log)
    /*
    {
      {status: "fulfilled", value: 1},
      {status: "rejected", reason: Error: error at ...}
    }
    */

마이크로태스크 큐

마이크로태스크 큐에는 Promise의 후속 처리 메서드의 콜백 함수가 일시 저장된다.
태스크 큐에는 그 외의 비동기 함수의 콜백 함수나 이벤트 핸들러가 일시 저장된다.

마이크로태스크 큐태스크 큐보다 우선순위가 높다.

이벤트 루프는 콜 스택이 비면 먼저 마이크로태스크 큐에서 대기하고 있는 함수를 가져와 실행하고, 이후 태스크 큐에서 대기하고 있는 함수를 실행한다.

setTimeout(() => console.log(1), 0);

Promise.resolve()
	.then(() => console.log(2))
	.then(() => console.log(3));

따라서 위의 코드는 2 → 3 → 1 순서로 출력된다.

0개의 댓글