자바스크립트는 비동기 처리를 위해 하나의 패턴으로 콜백 함수를 사용합니다. 콜백 패턴은 콜백 헬로 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하며 여러 개의 비동기 처리를 한번에 처리하는데도 한계가 있습니다.
“A promise is an object that may produce a single value some time in the future”
ES6부터는 비동기 처리를 위한 또 다른 패턴으로 프로미스를 도입했습니다. 프로미스는 자바스크립트 비동기 처리에 사용되는 객체라고 정의하고 있습니다.
먼저 콜백 패턴이 어떠한 단점이 있어서 프로미스가 생겨나게 됐는지에 대해 알아보고 프로미스에 대해 계속 알아보도록 하겠습니다.
콜백 패턴의 단점
콜백 헬
동기식 처리와 비동기식 처리에 대해 알고 있다는 가정하에 설명을 하겠습니다. 자바스크립트는 아시다시피 비동기식 처리 모델을 사용하고 있고 이러한 모델은 요청을 병렬로 처리하기 때문에 다른 요청이 블로킹되지 않는 장점이 있습니다.
하지만 비동기 처리를 위해 콜백 패턴을 사용하면 처리 순서를 보장하기 위해 콜백 함수가 중첩되어 복잡도가 높아지는데 이러한 현상을 콜백 헬이라고 합니다. 콜백 헬은 가독성을 나쁘게 하며 실수를 유발하는 원인이 됩니다.
step1(function(value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
step5(value4, function(value5) {
// value5를 사용하는 처리
});
});
});
});
});
에러 처리의 한계
try {
setTimeout(() => { throw new Error('Error!'); }, 1000);
} catch (e) {
console.log('에러를 캐치하지 못한다..');
console.log(e);
}
비동기 처리 함수의 콜백 함수는 해당 이벤트가 발생하면 태스트 큐(Task queue)로 이동한 후 호출 스택이 비어졌을 때, 호출 스택으로 이동되어 실행됩니다.setTimeout 함수는 비동기 함수이므로 콜백 함수가 실행될 때까지 기다리지 않고 즉시 종료되어 호출 스택에서 제거됩니다. 이후 tick 이벤트가 발생하면 setTimeout 함수의 콜백 함수는 태스트 큐로 이동한 후 호출 스택이 비어졌을 때 호출 스택으로 이동되어 실행됩니다. 이때 setTimeout 함수는 이미 호출 스택에서 제거된 상태입니다. 이것은 setTimeout 함수의 콜백 함수를 호출한 것은 setTimeout 함수가 아니다라는 것을 의미합니다.
예외(exception)는 호출자(caller) 방향으로 전파되지만 위에서 살펴본 바와 같이 setTimeout 함수의 콜백 함수를 호출한 것은 setTimeout 함수가 아니기 때문에 setTimeout 함수의 콜백 함수 내에서 발생시킨 에러는 catch 블록에서 캐치되지 않아 프로세스는 종료됩니다.
프로미스의 생성
프로미스는 Promise 생성자 함수를 통해 인스턴스화합니다. Promise 생성자 함수는 콜백 함수를 인자로 전달 받는데 이 콜백 함수는 resolve와 reject함수를 인자로 전달받습니다.
// Promise 객체의 생성
const promise = new Promise((resolve, reject) => {
// 비동기 작업을 수행한다.
if (/* 비동기 작업 수행 성공 */) {
resolve('result');
}
else { /* 비동기 작업 수행 실패 */
reject('failure reason');
}
});
Promise는 비동기 처리가 성공했는지 실패했는지 등의 아래와 같은 상태 정보를 갖습니다.
상태 | 의미 | 구현 |
---|---|---|
pending | 비동기 처리가 아직 수행되지 않은 상태 | resolve 또는 reject 함수가 아직 호출되지 않은 상태 |
fulfilled | 비동기 처리가 수행된 상태 (성공) | resolve 함수가 호출된 상태 |
rejected | 비동기 처리가 수행된 상태 (실패) | reject 함수가 호출된 상태 |
settled | 비동기 처리가 수행된 상태 (성공 또는 실패) | resolve 또는 reject 함수가 호출된 상태 |
- pending(대기)
아래와 같이 new Promise() 메서드를 호출하면 대기 상태가 됩니다.
new Promise(function(resolve, reject) {
// ...
});
- fulfilled(이행)
비동기 처리가 성공했을 시 resolve 함수를 호출하면 이행 상태가 됩니다. 이행 상태가 되면 아래와 같이 then()을 이용하여 값을 받을 수 있고 후속 동작을 정의할 수 있습니다.
function getData() {
return new Promise(function(resolve, reject) {
var data = 100;
resolve(data);
});
}
// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function(resolvedData) {
console.log(resolvedData); // 100
});
- rejected(실패)
비동기 처리가 실패했을 시 reject를 호출하면 실패 상태가 됩니다. 실패한 이유, 에러에 대해서는 catch()를 사용해 받을 수 있습니다.
function getData() {
return new Promise(function(resolve, reject) {
reject(new Error("Request is failed"));
});
}
// reject()의 결과 값 Error를 err에 받음
getData().then().catch(function(err) {
console.log(err); // Error: Request is failed
});
프로미스 예제 코드
function getData() {
return new Promise(function(resolve, reject) {
$.get('url 주소/products/1', function(response) {
if (response) {
resolve(response);
}
reject(new Error("Request is failed"));
});
});
}
// 위 $.get() 호출 결과에 따라 'response' 또는 'Error' 출력
getData().then(function(data) {
console.log(data); // response 값 출력
}).catch(function(err) {
console.error(err); // Error 출력
});
위 코드는 서버에서 응답을 받아오면 resolve()를 호출하고 없으면 reject를 호출합니다. 호출된 메서드에 따라 then()이나 catch()로 분기하여 응답 결과 또는 오류를 출력합니다.
프로미스 체이닝(Promise Chaining)
비동기 처리를 하다보면 비동기 처리 안에 또 비동기 처리를 해야할 상황이 있습니다. 콜백 함수로 처리한 경우에는 콜백 함수가 중첩되어 콜백 헬이 발생했습니다. 하지만 프로미스는 이러한 상황에서 후속 처리 메소드를 체이닝하여 여러 개의 프로미스를 연결하여 사용할 수 있습니다.
then()메소드가 Promise 객체를 반환하도록 하면 여러 개의 프로미스를 연결하여 사용할 수 있습니다. 간단한 예를 아래와 같이 들 수 있습니다.
new Promise(function(resolve, reject){
setTimeout(function() {
resolve(1);
}, 2000);
})
.then(function(result) {
console.log(result); // 1
return result + 10;
})
.then(function(result) {
console.log(result); // 11
return result + 20;
})
.then(function(result) {
console.log(result); // 31
});
'프론트엔드 스터디 > Javascript' 카테고리의 다른 글
[RxJS] Operator - pipe, zip (0) | 2021.07.28 |
---|---|
RxJS 정리 (0) | 2021.07.27 |
자바스크립트의 이벤트 (0) | 2021.07.14 |
자바스크립트에서 객체지향 프로그래밍이란? (0) | 2021.07.14 |
자바스크립트 core 개념 간단 정리 (0) | 2021.07.13 |