[JavaScript] Promise

jhm·2022년 4월 14일
0
post-thumbnail

자바스크립트는 비동기 처리를 위해 콜백 함수를 이용한다. 콜백 패턴은 가독성이 나쁘고 에러의 처리가 곤란하며 여러 개의 비동기 처리를 한번에 처리하는 데 한계가 있다.

ES6부터는 이러한 콜백 패턴의 단점을 보완하는 프로미스를 도입했다. 프로미스는 콜백 패턴과 달리 비동기 처리 시점을 명확하게 표현할 수 있다는 장점이 있다.

Promise 객체 생성

Promise 생성자 함수는 인수로 두 개의 콜백 함수를 전달받는데 비동기 처리가 성공했을 때 호출할 resolve 함수와 비동기 처리가 실패했을 때 호출할 reject 함수이다.

const promise = new Promise((resolve, reject) => {
	if(/* 비동기 처리 성공 */) {
		resolve('success');
  	} else { /* 비동기 처리 실패 */
		reject('failure);
	}
});

Promise의 상태 정보

Promise의 상태 정보의미상태 변경 조건
Pending비동기 처리가 아직 수행되지 않은 상태프로미스 생성 직후 기본 상태
fulfilled비동기 처리가 성공한 상태(수행됨)resolve 함수 호출
rejected비동기 처리가 실패한 상태(수행됨)reject 함수 호출

fulfilled 또는 rejected 상태를 settled 상태라고 한다. settled 상태는 fulfilled 또는 rejected 상태에 관계없이 pending 상태가 아닌 비동기 처리가 수행된 상태를 말한다.

Promise는 처리 상태와 함께 처리 결과도 상태로 갖는다. pending 상태는 처리 결과로 undefined, fulfilled 상태는 처리 결과로 value, 마지막으로 rejected 상태는 처리 결과로 error를 가진다.

promise의 후속 처리 메서드

Promise의 비동기 처리 상태가 변화하면 이에 따른 후속 처리가 필요하다. 이를 위해 Promise는 후속 메서드로 then, catch, finally를 제공한다.

Promise의 비동기 처리 결과에 따라 후속 처리 메서드에 인수로 전달한 콜백 함수가 선택적으로 호출된다. 이때 후속 처리 메서드의 콜백 함수에 Promise의 처리 결과가 인수로 전달된다.

  • Promise.prototype.then
    then 메서드는 비동기 처리가 성공했을 때(fulfilled 상태) 호출되는 성공 처리 콜백 함수와 비동기 처리가 실패했을 때(rejected 상태) 호출되는 실패 처리 콜백 함수를 인수로 전달받는다.

    new Promise(resolve => resolve('fulfilled'))
        .then(v => console.log(v), e => console.error(e));
    new Promise((_, reject) => reject(new Error('rejected')))
        .then(v => console.log(v), e => console.error(e));
  • Promise.prototype.catch
    catch 메서드는 한 개의 콜백 함수를 인수로 전달받는다.
    catch 메서드의 콜백 함수는 Promise가 rejected 상태인 경우에만 호출된다.

    new Promise((_, reject) => reject(new Error('rejected')))
        .catch(e => console.error(e));
  • Promise.prototype.finally
    finally 메서드는 한 개의 콜백 함수를 인수로 전달받는다.
    finally 메서드의 콜백 함수는 Promise의 처리 상태(fulfilled or rejected)에 상관없이 무조건 한 번 호출된다.

    new Promise(() => {})
        .finally(() => console.error('finally'));

Promise의 에러 처리

콜백 패턴은 에러 처리가 곤란하다는 단점이 있다. Promise는 에러를 문제없이 처리할 수 있다.
비동기 처리에서 발생한 에러를 then 메서드의 두 번째 콜백 함수로 처리하는 방법이 있다.

하지만 이 방법은 첫 번째 콜백 함수에서 발생한 에러를 캐치하지 못하고 코드가 복잡해져 가독성이 떨어진다는 단점이 있기 때문에 비교적 가독성이 좋고 then 메서드 내부에서 발생한 에러까지 캐치할 수 있는 방법을 사용하는 것을 권장한다.

이 방법은 바로 catch 메서드를 호출하는 방법이다. catch 메서드를 모든 then 메서드를 호출한 이후에 호출하면 비동기 처리에서 발생한 에러와 then 메서드에서 발생한 에러까지 모두 캐치할 수 있다.

promiseGet(url)
	.then(res => console.xxx(res))  // then 메서드에서 발생한 에러도 캐치할 수 있다!
	.catch(err => console.error(err));

Promise 체이닝

콜백 패턴은 콜백 헬이라는 치명적인 단점이 있다. Promise는 then, catch, finally 후속 처리 메서드를 통해 콜백 헬을 해결했다.

Promise는 then, catch, finally 후속 처리 메서드는 항상 콜백 함수가 반환한 Promise를 반환하므로 연속적으로 호출할 수 있다. then 메서드를 연속으로 호출하여 비동기 처리 절차를 추가할 수 있다는 의미이다. 이를 Promise 체이닝이라 한다.

Promise는 Promise 체이닝을 통해 비동기 처리 결과를 전달받아 후속 처리를 하므로 콜백 헬이 발생하지 않는다. 다만 Promise도 콜백 패턴을 사용하므로 콜백 함수를 사용하지 않는 것은 아니다.

여전히 콜백 패턴은 가독성이 좋지 않다. 이 단점을 보완하기 위해 Promise 객체에서 더 나아가 ES8부터는 async/await를 도입했다. async/await를 사용하면 Promise의 후속 처리 메서드 없이 동기 처리와 거의 동일한 형태로 Promise가 처리 결과를 반환하도록 구현할 수 있다.

async/await에 대해서는 다음에 더 자세하게 알아보자.

profile
Front-End 개발 독학 중

0개의 댓글