콜백은 말 그대로 나중에 실행되는 코드를 의미한다.
ex) A()라는 함수가 있을 때, 인자로 어떤 함수를 넣어주었다고 하자. 자바스크립트에서 함수는 일급 객체이기 때문에 인자로 함수를 넣어주는 것이 가능.
function add(x, y){
return x + y;
}
function printResult(result){
console.log(result);
}
printResult(add(10,20));
위 코드를 콜백함수로 구현한다면? 다음과 같다.
function add(x, y, print){
print(x + y);
}
function printResult(result){
console.log(result);
}
add(10, 20, printResult);
add()함수에 콜백함수를 받을 print라는 파라미터를 추가하고,
내부에서 x+y를 인자로 전달해 준다.
그리고 print 파라미터의 인자로 printResult()함수를 전달해 주었다.
바로 여기서 printResult()함수가 콜백함수가 되는 것!!
먼저 add()함수가 호출된 후 printResult()함수가 add()함수 내부에서 나중에 호출된다.
콜백함수는 익명 함수도 인자로 전달이 가능하다.
function add(x,y, print){
print(x+y);
}
add(10, 20, (result) => {
console.log(result);
})
add()함수를 실행할 때 파라미터의 인자로 새로운 함수를 지정해주었다.
(1) function(result){console.log(result)}함수가 print라는 파라미터가 되고
(2) print(x+y)자체가 함수가 되어 x+y가 result값이 되고
(3) 콘솔에 x+y를 한 result값인 30이 출력되는 것.
.
.
콜백이 유용한 이유는 하나의 함수를 여러가지로 응용할 수 있다는 것이다.
function introduce (lastName, firstName, callback) {
var fullName = lastName + firstName;
callback(fullName);
}
function say_hello (name) {
console.log("안녕하세요 제 이름은 " + name + "입니다");
}
function say_bye (name) {
console.log("지금까지 " + name + "이었습니다. 안녕히계세요");
}
introduce("홍", "길동", say_hello);
// 결과 -> 안녕하세요 제 이름은 홍길동입니다
introduce("홍", "길동", say_bye);
// 결과 -> 지금까지 홍길동이었습니다. 안녕히계세요
위와 같이 다른 동작을 수행하는 함수 두개를 정의해두고 introduce()함수에 인풋으로 사용하면 introduce()함수에서 받아들이는 같은 인풋을 가지고 다른 동작 수행이 가능해진다!
즉, 함수를 나눠줌으로써 코드를 재활용할 수 있고, 관리도 더 쉬워진다!!
.
.
<콜백함수 사용 원칙>
- 익명의 함수 사용
함수 내부에서 실행되기 때문에 이름 붙이지 X
- 함수의 이름만 넘기기
function whatYourName(name, callback) { //함수지만 callback()으로 넘기지 X console.log('name: ', name); callback(); } function finishFunc() { console.log('finish function'); } whatYourName('miniddo', finishFunc); //함수지만 finishFunc()으로 넘기지 X /* name: miniddo finish function */
함수를 인자로 사용할 때 callback, finishFunc처럼 ()붙일 필요 X
.
.
콜백 함수를 잘 사용하면 비동기 처리의 장점을 극대화할 수 있지만 잘못 사용하면 콜백 지옥에 빠진다는것....주의하자😂(넘 어렵)
.
.
콜백 지옥을 벗어날 수 있도록 도와줄 Promise를 배워보자!
promise는 코드의 중첩이 많아지는 콜백 지옥을 벗어날 수 있게 해주는 객체이다.
ex)'새로 출시되는 아이폰을 사고 싶은데, 출시 시간을 몰라...맨날 매시간 사이트에 접속해서 확인을 해야하니 시간도, 에너지도 낭비되네? 아이폰이 출시되었다고 알려주면 바로 아이폰을 살텐데'
- 아이폰이 출시되었는지 계속 확인 -> 어떤 작업에 대한 요청에 비유
- 아이폰을 구매한 행동 -> 어떤 로직이 요청에 대한 응답을 받은 후 실행한 함수를 비유
- 애플에서 아이폰이 출시되면 문자를 보내주는 서비스를 제공할 시 이것이 -> Promise(알림을 보내준다고 약속을 하는 것과 같은 역할)!!!
Promise란
= 아이폰이 출시되면(응답이 오면) 문자를 보내주어(Promise) 바로 구매하도록(응답 후 실행할 함수) 해주는 문자 서비스!
=> 보낸 요청에 대해 응답이 준비되었을 때 알림을 주는 알리미 역할
.
.
.
[일반 비동기 함수]
function work(sec, callback){
setTimeout(() => {
callback(new Date().toISOString());
}, sec*1000);
};
work(1, (result) => {
console.log('첫 번째 작업', result);
});
work(1, (result) => {
console.log('두 번째 작업', result);
});
work(1, (result) => {
console.log('세 번째 작업', result);
});
첫 번째 -> 두 번째 -> 세 번째 작업 순으로 일을 하고 싶은데 위처럼 코드를 짜면 1,2,3 모두 같은 시간에 끝난 것을 알 수 있다.
[동기적 처리]
function work(sec, result){
setTimeout(() => {
cancelIdleCallback(new Date().toISOString());
}, sec*1000 );
};
work(1, (result) => {
console.log('첫 번째 작업', result);
work(1, (result) => {
console.log('두 번째 작업', result);
work(1, (result) => {
console.log('세 번째 작업', result);
});
});
});
[동기적 처리2]
function work(sec, callback){
setTimeout(() => {
callback(new Date().toISOString());
}, sec*1000);
};
work(1, (result) => {
console.log('첫 번째 작업', result);
work(1, (result) => {
work(1, (result) => {
console.log('세 번째 작업', result);
});
console.log('두 번째 작업', result);
});
});
위 두 예제의 결과는 모두 1->2->3 순으로 올바르게 작업을 할 수 있다.
.
.
하지만 이렇게 하면 눈으로 코드를 보며 로직을 바꿀 때 어떻게 해야하는지, 결과가 어떻게 될지, 확인하기 어려워짐. 이때 필요한 것이 Promise!
function workP(sec){
//Promise의 인스턴스를 반환하고
//then에서 반환한 것을 받는다.
return new Promise((resolve, reject) => {
//Promise 생성 시 넘기는 callback = resolve, reject
//resolve 동작 완료시 호출, 오류 났을 경우 reject
setTimeout(() => {
resolve(new Date().toISOString());
}, sec * 1000);
});
}
workP(1).then((result) => {
console.log('첫 번째 작업', result);
return workP(1);
}).then((result) => {
console.log('두 번째 작업', result);
});