[4장] 콜백 함수
1. 콜백 함수란?
- 콜백함수: 다른 코드의 인자로 넘겨주는 함수
- 콜백함수는 제어권과 관련이 깊음
- 어떤 함수 X를 호출하면서 특정 조건일 때 함수 Y를 실행해서 나에게 알려달라는 요청을 보내는 것임.
2. 제어권
📍호출시점
❓제어권
실행흐름 or 프로세스
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초)
// 0 (0.9초)
// 1 (1.2초)
// 0 (1.5초)
code | 호출 주체 | 제어권 |
---|---|---|
cbFunc() | 사용자 | 사용자 |
setInterval(cbFunc, 300) | setInterval | setInterval |
📍인자
map 메서드는 첫 번째 인자로 callback함수를 받고, 생략 가능한 두 번째 인자로 콜백함수 내부에서 this로 인식할 대상을 특정할 수 있다.
const numbers = [1, 2, 3];
numbers.map(function(currentValue, index) {
console.log(`Index ${index}: ${currentValue}`);
});
// Index 0: 1
// Index 1: 2
// Index 2: 3
// 예시 배열
const numbers = [1, 2, 3, 4, 5];
// map 메서드를 사용하여 각 요소를 제곱한 새로운 배열을 생성하는 예시
const squaredNumbers = numbers.map(function(currentValue, index, array) {
console.log(`Current Value: ${currentValue}, Index: ${index}, Array: ${array}`);
return currentValue * currentValue;
});
console.log(squaredNumbers); // [1, 4, 9, 16, 25]
📍this
콜백 함수도 함수이기 때문에 기본적으로 this가 전역객체를 참조하지만, 제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조하게 된다.
console.log(this === window); // true (전역 컨텍스트에서 this는 window 객체를 가리킴)
function sayHello() {
console.log(`Hello, ${this.name}!`);
}
const person = {
name: 'bori',
greet: sayHello
};
sayHello(); // Hello, undefined! (this는 전역 객체(window)를 가리킴)
person.greet(); // Hello, bori! (this는 person 객체를 가리킴)
---
<button id="myButton">Click me</button>
document.getElementById('myButton').addEventListener('click', function() {
console.log(this); // 이벤트가 발생한 요소(<button id="myButton">)
this.textContent = 'Clicked!'; // this를 사용하여 요소의 텍스트를 변경
});
3. 콜백함수는 함수
- 콜백 함수는 함수다.
- 콜백 함수로 어떤 객체의 매서드를 전달하더라도 그 메서드는 메서드가 아닌 함수로서 호출된다.
var obj = {
vals: [1, 2, 3],
logValues: function (v, i) {
console.log(this, v, i);
},
};
obj.logValues(1, 2); // { vals: [1, 2, 3], logValues: f } 1 2
[4, 5, 6].forEach(obj.logValues);
// Window { ... } 4 0
// Window { ... } 5 1
// Window { ... } 6 2
- this는 호출된 메서드가 속한 객체인 obj를 가리킴 →
{ vals: [1, 2, 3], logValues: f } 1 2
- forEach 메서드는 콜백 함수 내에서 this를 설정하지 않는다. 단순히 함수 자체로 전달되기 때문에 this는 전역 객체가 된다.
4. 콜백 함수 내부의 this에 다른 값 바인딩하기
🤔위의 해결 방법?!!
- 콜백 함수 내부의 this에 다른 값을 바인딩 하는 방법 - 전통적인 방식
var obj1 = { name: 'obj1', func: function () { var self = this return function () { console.log(self.name) } }, } var callback = obj1.func() setTimeout(callback, 1000)
2. 콜백 함수 내부에서 this를 사용하지 않은 경우
var obj1 = {
name: 'obj1',
func: function () {
console.log(obj1.name)
},
}
setTimeout(obj1.func, 1000)
3. 콜백 함수 내부의 this에 다른 값을 바인딩 하는 방법 - bind 메서드 활용 👍🏻👍🏻
→ forEach의 각 콜백 함수에서 this가 obj 객체를 가리키게 된다!
[4, 5, 6].forEach(obj.logValues.bind(obj))
5. 콜백 지옥과 비동기 제어
콜백 지옥: 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상
❓동기
현재 실행 중인 코드가 완료된 후에야 다음 코드를 실행하는 방식
❓비동기
현재 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어감
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,
"에스프레소"
);
🤔위의 해결 방법?!!
- 기명 함수로 변환
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, "에스프레소");
이 방식은 코드의 가독성을 높일뿐 아니라 함수 선언과 함수 호출만 구분할 수 있다면 위에서부터 아래로 순서대로 읽는 것도 쉽다. 하지만 일회성 함수에 전부 변수를 할당하는 것은 번거롭게 느껴진다.
- Promise
new Promise(function (resolve) {
setTimeout(function () {
var name = "에스프레소";
console.log(name);
resolve(name);
}, 500);
})
.then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ", 아메리카노";
console.log(name);
resolve(name);
}, 500);
});
})
.then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ", 카페모카";
console.log(name);
resolve(name);
}, 500);
});
})
.then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ", 카페라떼";
console.log(name);
resolve(name);
}, 500);
});
});
new 연산자와 함께 호출한 Promise의 인자로 넘겨주는 콜백 함수는 호출할 때 바로 실행되지만 그 내부에 resolve 또는 reject 함수를 호출하는 구문이 있을 경우 둘 중 하나가 실행되기 전까지는 다음(then) 또는 오류 구문(catch)으로 넘어가지 않는다.
- 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();
- ‘*’이 붙은 함수가 바로 Generator 함수
- `yield` 키워드를 사용하여 비동기 작업을 기다리고, `setTimeout`을 통해 커피 이름을 순차적으로 추가한다.
- Promise + 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();
함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 표기하는 것만으로 뒤의 내용을 Promise로 자동 전환하고, 해당 내용이 resolve된 이후에야 다음으로 진행한다. (Promise의 then과 흡사한 효과..)
'📖 책 찢기 > 코어 자바스크립트' 카테고리의 다른 글
[코어자바스크립트] 6장. 프로토타입 (0) | 2024.06.18 |
---|---|
[코어자바스크립트] 5장. 클로저 (0) | 2024.06.17 |
[코어자바스크립트] 3장. this (0) | 2024.06.14 |
[코어 자바스크립트] 2장. 실행 컨텍스트 (3) | 2024.06.11 |
[코어 자바스크립트] 1장. 데이터 타입 (0) | 2024.06.10 |