Promise
는 기존의 비동기 처리에서 콜백 헬을 해결하기 위해 나온 ECMAScript 표준 사양입니다. Promise
이전에 비동기 처리를 하기 위해서는 비동기 처리가 완료되면 콜백 함수를 실행하는 방식을 사용하였습니다. 비동기로 가져온 데이터는 외부에서 사용할 수 없기 때문입니다. 이유는 비동기를 처리하는 브라우저의 방식 때문입니다. 이에 대해서는 다른 장에서 자세히 다루도록 합니다.
var asyncFn = requestData(apiURL, callback(data) => {
requestData(apiURL, callback(data) => {
reqeustData(apiURL, callback(data) => {
// ...
}
}
})
이런 식으로 작성하게 되면 코드를 작성하기 힘들고 읽고 분석하는 데에도 많은 어려움이 있습니다. 가장 큰 문제는 에러 처리가 곤란하다는 점입니다.
try {
setTimeout(() => { throw new Error('Error!!'); }, 1000);
} catch (error) {
// 에러가 잡히지 않음
console.error('에러', error);
}
왜 catch
에서 에러를 잡지 못한 것일까요? 기본적으로 error
는 호출자에게 전파됩니다. 즉, 자신을 호출한 함수로 전달된다는 의미입니다. 하지만 setTimeout
과 같은 비동기 함수는 평가됐을 때 Web API
로 처리가 넘어가게 되고 콜 스택이 모두 비워진 다음 완료된 결과를 이벤트 루프를 통해 콜 스택에 전달되게 됩니다. 이는 setTimeout
의 콜백 함수의 호출자는 setTimeout
이 아니게 된다는 말입니다. 따라서 catch로 잡을 수 없게 됩니다. 프로미스는 이러한 문제를 해결했습니다.
사용법
프로미스는 new
를 통해 생성합니다. 그리고 반환값은 프로미스 객체가 됩니다.
const promise = new Promise((resolve, reject) => { return resolve(data); });
프로미스의 상태는 세 가지의 상태로 관리합니다.
pending
: 비동기 처리가 완료되지 않은 상태fulfilled
: 비동기 처리가 정상적으로 완료된 상태rejected
: 비동기 처리가 실패한 상태
먼저 프로미스를 실행하면 pending 상태가 됩니다. 그리고 비동기 처리가 진행되며 그 결과에 따라 프로미스의 상태가 변합니다.
- 비동기 처리 성공 :
resolve
함수를 호출하여fulfilled
상태로 변경함 - 비동기 처리 실패 :
reject
함수를 호출하여rejected
상태로 변경함
// resolve 함수가 호출되면 pending 상태였던 프로미스 객체는 fulfilled 상태로 변경됨
const fulfilled = new Promise(resolve => resolve(1));
// reject 함수가 호출되면 pending 상태였던 프로미스 객체는 rejected 상태로 변경됨
const rejected = new Promise((_, rejected) => reject(new Error("에러 발생")));
후속 메서드
프로미스를 통해서 처리한 데이터는 후속 메서드를 사용하여 이용할 수 있습니다.
then
catch
finally
then
then
메서드는 두 개의 콜백 함수를 인자로 받습니다.
- 첫 번째 콜백 함수 - 전달된 프로미스의 상태가
fulfilled
인 경우 호출됩니다. 이때 콜백 함수의 인자는 전달된 프로미스의 결과를 받습니다. - 두 번째 콜백 함수 - 전달된 프로미스의 상태가
rejected
인 경우 호출됩니다. 이때 콜백 함수의 인자는 전달된 프로미스의 에러를 받습니다. - 반환값은 언제나 프로미스 객체입니다.
// fulfilled
new Promise((resolve) => resolve(1))
.then((value) => console.log(value)); // 1
// rejected
new Promise((_, reject) => reject(new Error("에러 발생"))
.then((value) => console.log(value), (err) => console.error(err));
catch
catch
메서드는 한 개의 콜백 함수를 인자로 받습니다. 콜백 함수는 프로미스의 상태가 rejected
인 경우에만 실행됩니다. then(undefined, reject)와 동일하게 동작합니다. 이 메서드 역시 언제나 프로미스 객체를 반환합니다.
new Promise((_, reject) => reject(new Error("에러 발생"))
.catch((err) => console.error(err));
// 위와 같음
// new Promise((_, reject) => reject(new Error("에러 발생"))
// .then((value) => console.log(value), (err) => console.error(err));
finally
finally
메서드는 한 개의 콜백 함수를 인자로 받습니다. 콜백 함수는 fulfilled
나 rejected
상태와 상관없이 무조건 한 번 호출됩니다. 비동기 처리의 성공이나 실패와 상관없이 실행해야 하는 코드를 작성해야 할 때 유용합니다. 이 메서드도 언제나 프로미스 객체를 반환합니다.
new Promise(() => {})
.finally(() => console.log('finally')); // finally