🐕 콜백함수(callback function)는 다른 코드의 인자로 넘겨주는 함수이다.
콜백 함수를 넘겨받은 코드는 이 콜백함수를 필요에 따라 적절한 시점에 실행할 것이죠!
콜백함수에 대해 알아보기 전에 비동기와 동기에 대해서 간략하게 알아보고 넘어가겠습니다!
비동기와 동기🍜🍜
- 동기적(Synchronous)
현재 실행중인 코드가 완료된 후에야 다음코드를 실행하는 방식
ex. 짜장면을 배달 → 다 먹을때까지 기다림 → 짜장면집으로 돌아간다console.log('배달 출발'); console.log('배달 완료!'); console.log('손님이 짜장면을 다 먹기를 기다린다...'); console.log('1시간 후...'); console.log('그릇을 찾은 후 가게로 돌아온다.');
- 비동기적(Asychronous)
코드는 현재 실행중인 코드의 완료여부와 무관하게 즉시 다음으로 넘어간다.
cpu의계산에 의해 즉시 처리가 가능한 대부분의 코드는 동기적인 코드
ex. 짜장면을 배달 → 손님이 먹는동안 다른일 하러 떠남 → 다 먹은 뒤 그릇을 찾아서 돌아감console.log('배달 출발'); console.log('배달 완료!'); setTimeout(() => { console.log('1시간 후...'); console.log('그릇을 찾은 후 가게로 돌아온다.'); }, 1000); console.log('가게로 돌아간다.'); console.log('설거지를 한다..'); console.log('서빙을 한다.'); console.log('밥을 먹는다.'); console.log('후식을 먹는다.');
위 코드가 비동기적 코드가 될 수 있는 이유는 무엇일까요? 바로 setTimeout(func, delay)
때문!
여기서 인자로 받는 func
가 우리가 공부하고 있는 callback Function
이다.
callback
이란? (되돌아와서 호출해라! 라는 명령이다.)
printArray
는 콜백함수고 clearInterval
에게 제어권을 넘겨주었기 때문에printArray
가 아니라 setInterval
이 호출시점을 결정하게 됨.const arr = ['무', '야', '호'];
const printArray = () = {
console.log(arr.shift());
if(!arr.length) {
clearInterval(timer);
}
};
const timer = setInterval(printArray, 1000);
바로 콜백 지옥(callback Hell)🔥때문!
setTimeout(function(name){
var coffeeList = name;
console.log(coffeeList);
setTimeout(function(name){
coffeeList += ', ' + name;
setTimeout(function(name){
coffeeList += ', ' + name;
setTimeout(function(name){
coffeeList += ', ' + name;
}, 500, 'cafeLatte');
}, 500, 'cafeMocha');
}, 500, 'americano');
}, 500, 'espresso');
위 코드는 각 callback이 커피이름을 전달하고 이름을 추가해주는 코드입니다.
목적달성에는 지장이 없지만 들여쓰기 수준이 과도하게 깊어졌을 뿐만 아니라
순서가 아래에서 위로 향하고 있어 어색하게 느껴집니다..
⇒ 가독성문제와 어색함을 동시에 해결하기 위하여 익명의 콜백함수를 모두 기명함수로 바꿔봄
var coffeeList = '';
var addEspresso = function(name){
coffeeList = name;
console.log(coffeeList);
setTimeout(addAmericano, 500, 'americano');
};
var addAmericano = function(name){
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(addMocha, 500, 'cafeMocha');
};
var addMocha = function(name){
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(addLatte, 500, 'cafeLatte');
};
var addLatte = function(name){
coffeeList += ', ' + name;
console.log(coffeeList);
};
setTimeout(addEspresso, 500, 'espresso');
이 방식은 코드의 가독성을 높일뿐 아니라 함수 선언과 함수 호출만 구분할 수 있다면 위에서 아래로 순서대로 읽어내려가는데 어려움이 없다.
하지만... 일회성 함수를 모두 변수에 할당하는 것이 마뜩찮을 수도?
그래서 ES6에서는Promise
, Generator
가 나왔지롱
new Promise(function(resolve) {
setTimeout(function() {
var name = 'espresso';
console.log(name);
resolve(name);
}, 500);
}).then(function(prevName) {
return new Promise(function(resolve){
setTimeout(function() {
var name = prevName + ', americano';
console.log(name);
resolve(name);
}, 500)
});
}).then(function (prevName){
return new Promise(function (resolve){
setTimeout(function() {
var name = prevName + ', cafeMocha';
console.log(name);
resolve(name);
}, 500);
});
}).then(function(prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', cafeLatte';
console.log(name);
resolve(name);
}, 500);
});
});
var addCoffee = function(name) {
return function(preName) {
return new Promise(function (resolve) {
setTimeout(function() {
var newName = preName ? (preName + ', ' + name) : name;
console.log(newName);
resolve(newName);
}, 500);
});
};
};
addCoffee('에스프레소')()
.then(addCoffee('아메리카노'))
.then(addCoffee('카페모카'))
.then(addCoffee('카페라떼'));
출력결과
여기서 잠깐.. 😅 프로미스가 몬데..!!!
🐕 Promise란..?
ES6에서 추가된 JavaScript 내장 객체
프로미스란 자바스크립트 비동기 처리에 사용되는 객체
resolve
는 성공,reject
는 실패를 반환한다!
왜 필요한가?
주로 서버에서 받아온 데이터를 화면에 표시할때 사용한다.
일반적으로 웹 애플리케이션을 구현할 때 서버에서 데이터를 요청하고 받아오기 위하여 사용..
실제 연산을 직접 처리해주는 것은 아니고, 해당 연산을 대리하여 결과나 실패를 처리하기 위한 처리기와 연결할 수 있도록 하는 객체
new Promise()
의 3가지 상태
대기(Pending)
먼저 아래와 같이 new Promise()
메서드를 호출하면 대기(Pending)상태가 된다.
new Promise();
new Promise()
를 호출할때 콜백함수를 선언할 수 있고, 콜백함수의 인자는 resolve
, reject
이다.
new Promise(function(resolve, reject) {
});
Fulfilled(이행) = 완료
여기서 콜백함수의 인자 resolve
를 아래와 같이 실행하면 이행(Fulfilled)상태가 된당.
new Promise(function(resolve, reject) {
resolve();
});
그리고 이행 상태가 되면 아래와 같이 then()
을 이용하여 처리 결과 값을 받을 수 있다!
function getData(){
return new Promise(function(resolve, reject) {
var data = 100;
resolve(data);
});
}
//resolve()의 결과값 data를 resolvedData로 받음
getData().then(function(resolvedData){
console.log(resolveData); //100
});
Rejected(실패)
new Promise()
로 프로미스 객체를 생성하면 콜백 함수 인자로 resolve
와 reject
를 사용할 수 있다고 했다. 여기서 reject를 아래와 같이 호출하면 실패 상태가 된다!
new Promise(function(resolve, reject) {
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
});
Generator
또 다른 비동기 작업의 동기적 표현으로 Generator
가 있다.
var addCoffee = function (preName, name){
setTimeout(function () {
coffeeMaker.next(preName ? preName + ', ' + name : name);
}, 500);
};
var coffeeGenerator = function* () {
var espresso = yield addCoffee('', '에스프레소');
console.log(espresso);
var americaon = yield addCoffee(espresso, '아메리카노');
console.log(americaon);
var mocha = yield addCoffee(americaon, '카페모카');
console.log(mocha);
var latte = yield addCoffee(mocha, '카페라떼');
console.log(latte);
};
var coffeeMaker = coffeeGenerator();
coffeeMaker.next();
위 코드에서 6번째 줄 ‘*’가 붙은 함수가 바로 Generator
함수
Generator
를 실행하면 Iterator
가 반환되는데 Iterator
는 next
라는 메서드를 가지고 있다.
이 next
를 호출하면 Generator
함수 내부에서 가장 먼저 등장하는 yield
에서 함수의 실행을 멈추게 되고, 이후 다시 next
메서드를 호출하면 앞서 멈췄던 부분 부터 시작해서 그 다음에 등장하는 yield
에서 함수의 실행을 멈춥니다.
⇒ 비동기 작업이 완료되는 시점마다 next
메서드를 호출해 준다면 Generator
함수 내부의 소스가 위에서 부터 아래로 순차적으로 진행된다.
🐕총정리
- 콜백함수는 다른 코드에 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수이다.
- 제어권을 넘겨받은 코드는 다음과 같은 제어권을 가진다.
1) 콜백함수를 호출하는 시점을 스스로 판단해서 실행합니다.
2) 콜백함수를 호출할 때 인자로 넘겨줄 값들 및 그 순서가 정해져 있습니다. 이 순서를 따르지 않고 코드를 작성하면 엉뚱한 결과를 얻게 됩니다.
3) 콜백함수의this
가 무엇을 바라보도록 할지가 정해져 있는 경우도 있습니다. 정하지 않은 경우에는 전역 객체를 바라봅니다. 사용자 임의로 this를 바꾸고 싶을 경우bind
메서드를 활용하면 됩니다.
- 어떤 함수에 인자로 메서드를 전달하더라고 이는 결국 함수로서 진행됩니다.
- 비동기 제어를 위해 콜백함수를 사용하다 보면 콜백지옥에 빠지기 쉽습니다. 최근의 ECMAScript에는
Promise
,Generator
,async/awit
등 콜백 지옥에서 벗어날 수 있는 훌륭한 방법들이 속속 등장하고 있다!