callback
JavaScript에서는 함수가 받아들이는 값을 int, string형 뿐만 아니라 함수로도 받아들일 수 있다. 인자로 받아들인 함수를 다시 호출하는 기능을 callback이라고 부른다.
function add5(a, callback) {
setTimeout(() => callback(a + 5), 100)
// 100ms가 지난 후 함수로 입력받은 callback에 a + 10값을 다시 입력하여 callback함수 호출
}
add5(10, function (res) { // add5가 입력받는 callback함수 정의 부분
console.log(res)
});
add5를 호출하는 부분을 보면 첫번재 인자로 숫자 5, 두번째로는 함수를 생성해서 전달하는 것을 볼 수 있다. add5 함수 내부에서는 입력 받은 함수 callback에 a+5를 헤서 다시 호출하게 된다.
callback이 처음에는 어렵지만 한번 이해하면 굉장히 편한 기능이다. 하지만 callback을 연속적으로 사용하면 유지보수가 힘들어진다는 치명적인 단점이 있다.
이번에는 위 예제에서 만든 add5를 연속적으로 호출해서 10에 5+5+5+5를 하는 코드이다.
add5(10, res => {
add5(res, res => {
add5(res, res => {
add5(res, res => {
console.log(res)
})
})
})
})
단순히 더하기만 한느 코드지만 함수를 재귀적으로 여러번 호출하기 때문에 복잡해 보인다. 실제로 로그인 기능을 구현할 때 로그인 정보를 받아들여 비밀번호를 확인하고, 옳다면 유저 정보를 가져오고 틀리다면 에러 메시지를 출력하는 등 if-else문이 callback 중간 중간에 들어가야 하는 경우가 자주 있다.
따라서 callback을 여러번 연속적으로 호출하게 될 경우 callback hell이 발생할 수 있다.
promise
promise란 기본적으로 callback이 하는 일과 같다.
다만 차이점이라면 promise는 작업이 끝난 후 실행할 함수를 제공하는 것이 아니라 promise 자체 메소드인 .then()을 호출한다.
function add10(a) {
return new Promise(resolve => setTimeout(() => resolve(a + 10), 100));
} //Promise사용 시 작업이 끝났음을 알려주는 resolve를 인자로 받아들임.
add10(10)
.then(add10)
.then(add10)
.then(add10)
.then((res) => console.log(res))
promise는 then()과 같은 메소드를 연속적으로 사용할 수 있는 이점을 가지고 있다.
따라서 callbackㅇㄹ 사용했을 때와는 다르게 코드를 작성할 수 있고, 이해가 한결 쉬워진다.
이처럼 메소드를 연속적으로 사용하는 chaining이 가능한 이유는 add10 함수가 결과값을 promise로 리턴하기 때문이다. 실제로 add10의 결과값을 출력해보면 값이 다음과 같이 출력되는 것을 볼 수 있다.
console.log(add10(10));
>> Promise { <pending> }
add10(10)
.then((res) => {
throw 'test error';
})
.catch((err) => console.log(err));
promise에서는 작업이 실패했을 경우 자동으로 .catch() 메소드가 호출된다.
따라서 callback처럼 함수 호출 중간에 if-else를 사용하는 것이 아닌 .catch()로 한번에 해결할 수 있다는 장점이 존재한다.
try {
add10(10)
.then((res) => {
throw 'test error';
})
} catch(err) {
console.log(err)
}
기존 try-catch를 이용해서도 예외 처리가 가능하지만 자바스크립트에서는 promise의 catch를 사용하라는 경고 메시지가 출력된다.