1. 콜백 함수란
- 다른 코드의 인자로 넘겨주는 함수. 즉, 콜백 함수를 넘겨 받는 코드(ex. forEach, setTimeout) 가 있다는 말.
- 콜백 함수를 넘겨 받은 코드가 콜백 함수를 필요에 따라 적절한 시점에 실행하게 된다.
- 콜백 함수는 다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로써 그 제어권도 함께 위임하는 함수. 콜백 함수를 위임받은 코드는 자체적으로 내부 로직에 의해 콜백 함수를 적절한 시점에 실행.
2. 제어권
- 호출 시점
==> 콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가진다. - 인자
==> map 함수는 각 배열 요소를 변환하여 새로운 배열을 반환한다. 기존 배열을 변경하지 않고, 새로운 배열을 생성.
// map 함수에 의해 새로운 배열을 생성해서 newArr에 담고 있네요!
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 ]
// map 함수에 의해 새로운 배열을 생성해서 newArr에 담고 있네요!
var newArr2 = [10, 20, 30].map(function (index, currentValue) {
console.log(index, currentValue);
return currentValue + 5;
});
console.log(newArr2);
// -- 실행 결과 --
// 10 0
// 20 1
// 30 2
// [ 5, 6, 7 ]
==> index - currentValue의 의미를 사람처럼 이해할 수 없다. 따라서 원하는 배열을 얻고자 한다면 정의된 규칙대로 작성해야 한다. 모든 것은 전적으로 map메서드. 즉, 콜백 함수를 넘겨받은 코드에게 그 제어권이 있다. 인자까지도 제어권이 넘겨받은 코드에 있다.
- this
==> 제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.
// Array.prototype.map을 직접 구현해봤어요!
Array.prototype.mapaaa = function (callback, thisArg) {
var mappedArr = [];
for (var i = 0; i < this.length; i++) {
// call의 첫 번째 인자는 thisArg가 존재하는 경우는 그 객체, 없으면 전역객체
// call의 두 번째 인자는 this가 배열일 것(호출의 주체가 배열)이므로,
// i번째 요소를 넣어서 인자로 전달
var mappedValue = callback.call(thisArg || global, this[i]);
mappedArr[i] = mappedValue;
}
return mappedArr;
};
const a = [1, 2, 3].mapaaa((item) => {
return item * 2;
});
console.log(a);
==> 제어권을 넘겨받을 코드에서 call/apply 메서드의 첫 번째 인자에서 콜백 함수 내부에서 사용될 this를 명시적으로 binding하기 때문에 this에 다른 값이 담길 수 있다.
- 콜백 함수로 어떤 객체의 메서드를 전달하더라도, 그 메서드는 메서드가 아닌 함수로 호출한다.
3. 콜백 함수 내부의 this에 다른 값 바인딩하기
// 전통적 방식
var obj1 = {
name: 'obj1',
func: function() {
var self = this; //이 부분!
return function () {
console.log(self.name);
};
}
};
// ---------------------------------
// obj1의 func를 직접 아래에 대입해보면 조금 더 보기 쉽습니다!
var obj2 = {
name: 'obj2',
func: obj1.func
};
var callback2 = obj2.func();
setTimeout(callback2, 1500);
// 역시, obj1의 func를 직접 아래에 대입해보면 조금 더 보기 쉽습니다!
var obj3 = { name: 'obj3' };
var callback3 = obj1.func.call(obj3);
setTimeout(callback3, 2000);
==> 조금 번거롭긴 해도 this를 우회적으로 활용하여 원하는 객체를 바라보게 할 수 있다.
==> 이런 부분을 아주 쉽게 해결할 수 있다. bind 메서드를 이용하는 방법이다.
var obj1 = {
name: 'obj1',
func: function () {
console.log(this.name);
}
};
//함수 자체를 obj1에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj1로 고정해줘!
setTimeout(obj1.func.bind(obj1), 1000);
var obj2 = { name: 'obj2' };
//함수 자체를 obj2에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj2로 고정해줘!
setTimeout(obj1.func.bind(obj2), 1500);
4. 동기 vs 비동기
- 동기: synchronous
==> 현재 실행중인 코드가 끝나야 다음 코드를 실행하는 방식
==> CPU의 계산에 의해 즉시 처리가 가능한 대부분의 코드는 동기적 코드
==> 계산이 복잡해서 CPU가 계산하는 데에 오래 걸리는 코드 역시도 동기적 코드 - 비동기: a + synchronous => async라고들 흔히 부른다.
==> 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식
==> setTimeout, addEventListner 등
==> 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 모두 비동기적 코드 - 웹의 복잡도가 올라갈 수록 비동기적 코드의 비중이 늘어난다.
==> 따라서 자바스크립트에서는 비동기적인 작업을 동기적으로 처리해주는 장치를 계속해서 마련해주고 있다.
==> Promise, Generator, async/await 같은 것들이 있다. - Promise
- 비동기 처리에 대해, 처리가 끝나면 알려달라는 약속
- new 연산자로 호출한 Promise의 인자로 넘어가는 콜백은 바로 실행
- 그 내부의 resolve(또는 reject) 함수를 호출하는 구문이 있을 경우 resolve(또는 reject) 둘 중 하나가 실행되기 전까지는 다음(then), 오류(catch)로 넘어가지 않는다
- 따라서, 비동기작업이 완료될 때 비로소 resolve, reject 호출
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);
});
});
- Generator
- *가 붙은 함수가 제너레이터 함수. 제너레이터 함수는 실행하면, Iterator 객체가 반환(next()를 가지고 있다.)
- iterator은 객체는 next 메서드로 순환 할 수 있는 객체
- next 메서드 호출 시, Generator 함수 내부에서 가장 먼저 등장하는 yield에서 stop 이후 다시 next 메서드를 호출하면 멈췄던 부분 => 그 다음의 yield까지 실행 후 stop
- 비동기 작업이 완료되는 시점마다 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();
- Promise + Async/await
- 비동기 작업을 수행코자 하는 함수 앞에 async 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await을 붙여주면 된다.
- Promise ~ then과 동일한 효과를 얻을 수 있다.
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();
'JavaScript Dev. > Javascript' 카테고리의 다른 글
JavaScript 자료구조 (0) | 2023.12.10 |
---|---|
14. DOM과 클래스, 클로저 (0) | 2023.10.28 |
12. this(정의, 활용 방법, 바인딩, call, apply, bind) (1) | 2023.10.19 |
11. 실행 컨텍스트(스코프, 변수, 객체, 호이스팅) (0) | 2023.10.11 |
10. 데이터 타입 (0) | 2023.04.21 |