[JavaScript] 콜백 함수 (Callback Function)
카테고리 : 프로그래밍 언어
태그: javascript · 자바스크립트 · 콜백 함수 · Callback Function · setInterval · Array.prototype.map · 콜백지옥 · 동기 · 비동기 · 콜백 지옥 해결 · ES6 · Promise · resolve · reject · then · Generator · ES2017 · async/await
태그: javascript · 자바스크립트 · 콜백 함수 · Callback Function · setInterval · Array.prototype.map · 콜백지옥 · 동기 · 비동기 · 콜백 지옥 해결 · ES6 · Promise · resolve · reject · then · Generator · ES2017 · async/await
1. 콜백 함수 (Callback Function)
- 다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수
2. 제어권
(1) 호출 시점
첫번째로 setInterval
구조를 알아야함
var intervalID = scope.setInterval(func,delay[,param1,param2,...]);
- 매개변수로
func
(함수) ,delay
(ms)값은 필수값 - 세 번째
param1
,param2
(매개변수)는 선택값
var count = 0;
var cbFunc = function () {
console.log(count);
if (++count > 4) clearInterval(timer);
};
var timer = setInterval(cbFunc, 300);
// 0 => (0.3초)
// 1 => (0.6초)
// 2 => (0.9초)
// 3 => (1.2초)
// 4 => (1.5초)
[Done] exited with code=0 in 1.558 seconds
코드 실행 방식과 제어권을 알아보면
- timer 변수는 setInterval의 ID값이 담김
**cbFunc
함수는 0.3초 마다 실행 , callback 내부에서 count값을 출력하고 , count값을 1만큼 증가하고 4보다 크면 루프 종료**
code | 호출 주체 | 사용자 |
---|---|---|
cbFunc | 사용자 | 사용자 |
setInterval(cbFunc, 300) | setInterval | setInterval |
- 이 코드는 제어권을 위임 받은
setInterval
이 스스로의 판단에 따라 적절한 시점(0.3초)에 익명 함수를 실행 한것
(2) 인자
(1) Array.prototype.map
예시
Array.prototype.map(callback[,thisArg]) callback:function(currentValue,index,array)
- map 메서드는 첫 번째 인자는 callback 함수 , 생략가능한 두 번째 인자로 콜백 함수 내부에서 인식할
this
- 콜백 함수의 첫 번째 인자는 배열 요소중 현재값 , 두번째 인자는 현재값의 인덱스 세번째 인자에선
map
메서드의 대상이 되는 배열
var newArr = [10, 20, 30].map(function (currentValue, index) {
console.log(currentValue, index);
return currentValue + 5;
});
console.log(newArr);
// 10 0
// 20 1
// 30 2
// [ 15, 25, 35 ]
[Done] exited with code=0 in 0.065 seconds
(3) this
Array.prototype.map = function (callback, thisArg) {
var mappedArr = [];
for (var i = 0; i < this.length; i++) {
var mappedValue = callback.call(thisArg || window, this[i], i, this);
mappedArr[i] = mappedValue;
}
return mappedArr;
};
- 첫번째 인자에 메서드
this
의 배열i
번째값 - 두번째 인자에
i
값 - 세번째 인자에는 배열 자체를 지정해 호출한다
결과값으로 mappedValue
에 담겨 mappedArr
의 i
번째 인자에 할당됌
3. 메서드를 콜백 함수로 전달할 경우
var obj = {
vals: [1, 2, 3],
logValues: function (v, i) {
console.log(this, v, i);
},
};
obj.logValues(1, 2);
[(4, 5, 6)].forEach(obj.logValues);
// { vals: [ 1, 2, 3 ], logValues: [Function: logValues] } 1 2
// Window {...} 4 0
// Window {...} 5 1
// Window {...} 6 2
- 메서드를
forEach
함수의 콜백 함수로 전달 forEach
에 의해 콜백이 함수로서 호출되고 , 별도로this
를 지정하는 인자를 지정하지 않았으니 함수 내부의this
는 전역객체를 바라봄
4. 콜백 함수 내부의 this 바인딩
(1): 콜백 함수 내부의 this
에 다른 값 바인딩 방법
var obj1 = {
name: "obj1",
func: function () {
var self = this;
return function () {
console.log(self.name);
};
},
};
var callback = obj1.func();
setTimeout(callback, 1000);
// obj1
[Done] exited with code=0 in 1.055 seconds
(2): Func
함수를 재활용 할 경우
var obj1 = {
name: "obj1",
func: function () {
var self = this;
return function () {
console.log(self.name);
};
},
};
var callback = obj1.func();
setTimeout(callback, 1000);
var obj2 = {
name: "obj2",
func: obj1.func,
};
var callback2 = obj2.func();
setTimeout(callback2, 1000);
var obj3 = { name: "obj3" };
var callback3 = obj1.func.call(obj3);
setTimeout(callback3, 2000);
callback2
에는obj2
의func
를 실행한 결과를 담아 콜백으로 사용callback3
에는obj1
의func
를 실행하면서this
를obj3
가 되도록 지정해 콜백 사용
(3): bind
메서드 활용
var obj1 = {
name: "obj1",
func: function () {
console.log(this.name);
},
};
setTimeout(obj1.func.bind(obj1), 1000);
var obj2 = { name: "obj2" };
setTimeout(obj1.func.bind(obj2), 1500);
5. 콜백지옥과 비동기 제어
- 콜백 지옥 : 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상
- 동기 코드 : 현재 실행 중인 코드가 끝나고 다음 코드를 실행하는 방식
- 비동기 코드 : 현재 실행 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어감
비동기 코드를 사용하는 경우 예시
- 사용자의 요청에 의해 특정 시간이 경과되기 전까지 어떤 함수의 실행을 보류하는 경우
- 사용자의 직접적인 개입이 있을때 비로소 어떤 함수를 실행하도록 대기
- 웹브라우저 자체가 아닌 별도의 대상에 무언가를 요청하고 그에 대한 응답이 왔을때, 어떤 함수를 실행하도록 대기
- 별도의 요청 , 실행 대기 , 보류 코드
(1) 0.5초 주기 마다 커피 목록 수집
(1) 콜백 지옥
setTimeout(
function (name) {
var coffeeList = name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += "," + name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += "," + name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += "," + name;
console.log(coffeeList);
},
500,
"카페라떼"
);
},
500,
"카페모카"
);
},
500,
"아메리카노"
);
},
500,
"에스프레소"
);
에스프레소
에스프레소,아메리카노
에스프레소,아메리카노,카페모카
에스프레소,아메리카노,카페모카,카페라떼
값 전달 순서가 아래에서 위로 향하고 있고 가독성이 매우 떨어진다
(2) 콜백 지옥 해결 - 기명 함수 변환
var coffeeList = "";
var addEspresso = function (name) {
coffeeList = name;
console.log(coffeeList);
setTimeout(addAmericano, 500, "아메리카노");
};
var addAmericano = function (name) {
coffeeList = name;
console.log(coffeeList);
setTimeout(addMocha, 500, "카페모카");
};
var addMocha = function (name) {
coffeeList = name;
console.log(coffeeList);
setTimeout(addLatte, 500, "카페라떼");
};
var addLatte = function (name) {
coffeeList = name;
console.log(coffeeList);
};
setTimeout(addEspresso, 500, "에스프레소");
에스프레소
아메리카노
카페모카
카페라떼
(3) 콜백 지옥 해결 - ES6 Promise
new
연산자와 함께 호출한Promise
의 인자로 넘겨주는 콜백 함수가 실행resolve
또는reject
함수를 호출하는 구문이 있을 경우 둘중 하나가 실행되기 전까지then
또는catch
로 넘어가지 않음
var addCoffee = function (name) {
return function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var newName = prevName ? prevName + "," + name : name;
console.log(newName);
resolve(newName);
}, 500);
});
};
};
addCoffee("에스프레소")()
.then(addCoffee("아메리카노"))
.then(addCoffee("카페모카"))
.then(addCoffee("카페라떼"));
에스프레소
에스프레소,아메리카노
에스프레소,아메리카노,카페모카
에스프레소,아메리카노,카페모카,카페라떼
(4) 콜백 지옥 해결 - ES6 Generator
*
이 붙은 함수가generator
함수generator
함수를 실행하면Iterator
가 반환되고Iterator
는next
라는 메서드를 가짐next
메서드를 호출하면generator
함수 내부에서 먼저 등장하는yierd
에서 함수 실행을 멈춤- 비동기 작업이 완료되는 시점마다
next
메서드를 호출하면generator
함수가 위에서 아래로 순차적으로 진행함
var addCoffee = function (prevName, name) {
setTimeout(function () {
coffeeMaker.next(prevName ? prevName + "," + name : name);
}, 500);
};
var CoffeeGenerator = function* () {
var espresso = yield addCoffee("", "에스프레소");
console.log(espresso);
var americano = yield addCoffee(espresso, "아메리카노");
console.log(americano);
var mocha = yield addCoffee(americano, "카페모카");
console.log(mocha);
var latte = yield addCoffee(mocha, "카페라떼");
console.log(latte);
};
var coffeeMaker = CoffeeGenerator();
coffeeMaker.next();
에스프레소
에스프레소,아메리카노
에스프레소,아메리카노,카페모카
에스프레소,아메리카노,카페모카,카페라떼
(5) 콜백 지옥 해결 - ES2017 async/await
- 비동기 작업을 수행할 함수 앞에
async
- 비동기 작업 실행 위치 마다
await
var addCoffee = function (name) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(name);
}, 500);
});
};
var coffeeMaker = async function () {
var coffeeList = "";
var _addCoffee = async function (name) {
coffeeList += (coffeeList ? "," : "") + (await addCoffee(name));
};
await _addCoffee("에스프레소");
console.log(coffeeList);
await _addCoffee("아메리카노");
console.log(coffeeList);
await _addCoffee("카페모카");
console.log(coffeeList);
await _addCoffee("카페라떼");
console.log(coffeeList);
};
coffeeMaker();
에스프레소
에스프레소,아메리카노
에스프레소,아메리카노,카페모카
에스프레소,아메리카노,카페모카,카페라떼