Promise
🧑🏻💻 Promise가 도입된 이유
✅ 비동기 함수에서의 callback 패턴의 문제점
- 비동기 함수를 호출하면 함수 내부의 비동기로 동작하는 코드가 완료되지 않았다 해도 기다리지 않고 즉시 종료된다. 이로인해 비동기 함수 내부의 비동기로 동작하는 코드에서 처리한 결과를 외부로 반환하거나 상위 스코프의 변수에 할당하면 기대한 대로 동작하지 않는다.
let g = 0;
setTimeout (() => { g = 100; }, 0);
console.log(g); // 0
예를 들면 비동기 함수인 setTimeout 함수는 콜백 함수의 처리 결과를 외부로 반환하거나 상위 스코프의 변수에 할당하지 못한다.
따라서 비동기 함수의 처리 결과에 대한 후속 처리 및 에러 관리를 비동기 함수 내부에서 수행해야 한다. 이때 사용되는 것이 콜백 함수인데, 후속 처리 후 또다시 비동기 함수를 호출해야 한다면 호출이 중첩되어 복잡도가 높아지는 콜백 지옥 현상이 발생한다.
이렇게 콜백 패턴으로 인해 나타나는 콜백 지옥 현상과 에러 처리 한계를 해결하고자 Promise가 도입되었다.
🧑🏻💻 Promise 사용하기
✅ 3가지 비동기 처리 상태
프로미스의 상태 정보 | 의미 | 상태 변경 조건 |
---|---|---|
pending | 비동기 처리가 아직 수행되지 않은 상태 | 프로미스가 생성된 직후 기본 상태 |
fulfilled | 비동기 처리가 수행된 상태(성공) | resolve 함수 호출 |
rejected | 비동기 처리가 수행된 상태(실패) | reject 함수 호출 |
✅ 후속 처리 메서드와 promise chaining
promiseGet('URL')
.then(res => console.log(res))
.catch(err => console.error(err))
.finally(() => console. log( 'Bye!'));
then() : 성공 처리 콜백 함수와 실패 처리 호출 함수 두 개의 콜백 함수를 인수로 전달받는다.
catch() : 프로미스가 rejectrd 상태 일 경우에 호출되는 한 개의 콜백 함수를 인수로 전달받는다.
finally() : 프로미스의 성공 여부에 상관없이 무조건 한 번 호출되는 한 개의 콜백 함수를 인수로 받는다.
then, catch, finally 후속 처리 메서드는 언제나 프로미스를 반환하므로 연속적으로 호출할 수 있다. 이를 promise chaining이라고 한다. 이를 활용하면 콜백 지옥 현상을 해결할 수 있다.
참고로 프로미스의 후속 처리 메서드의 콜백 함수는 마이크로태스크 큐에 일시 저장된다. 마이크로태스크 큐는 매크로태스크 큐 보다 실행 우선 순위가 높다.
✅ 흔한 사용법
//Promise 함수의 콜백 함수 내부에서 비동기 처리를 수행한다.
const promise = url => {
return new Promise((resolve, reject) => {
if (/* 비동기 처리 성공 */) {
resolve('result');
else { /* 비동기 처리 실패 */
reject('failure reason');
}
}
});
}
promise('URL')
.then(res => console.log(res))
.catch(err => console.error(err))
.finally(() => console. log( 'Bye!'));
Promise 생성자 함수가 인수로 전달받은 콜백 함수 내부에서 비동기 처리를 수행한다. 이때 비동기 처리가 성공하면 콜백 함수의 인수로 전달받은 resolve 함수를 호출하고, 비동기 처리가 실패하면 reject 함수를 호출한다.
이처럼 Promise는 비동기 처리 상태와 처리 결과를 관리하는 객체다.
🧑🏻💻 Web API fetch
const promise = fetch(url, [, option])
XMLHttpRequest 객체보다 사용법이 간단하다.
Promise를 지원하기 때문에 비동기 처리를 위한 콜백 패턴의 단점으로부터 자유롭다.
EI를 제외한 대부분의 모던 브라우저에서 지원한다.
fetch 함수는 HTTP 응답을 나타내는 Response 객체를 리핑한 객체를 반환한다.
에러 처리 시 유의할 점은, 잘못된 url 명시로 나타난 에러는 불리안으로 반환하기 때문에 if문을 사용하여 명시적으로 에러를 처리할 필요가 있다.
const wrongUrl = ('https://~')
//부적절한 URL이 지정되었기 때문에 404 Not Found 에러가 발생한다.
fetch(wrongUrl)
.then(response = {
if (!response.ok) throw new Error(response.statusText);
return response.json();
})
.then(todo => console.log(todo))
.catch(err => console.error(err))
- 참고로 axio는 모든 HTTP 에러를 reject하는 프로미스를 반환하기 때문에 모든 에러를 catch에서 처리할 수 있다.
참고 자료
- 모던 자바스크립트 Deep Dive (사진 출처)