Core js - 콜백함수

heyhey·2023년 10월 30일
0
post-thumbnail

1. 콜백 함수란

콜백 함수는 다른 코드의 인자로 넘겨주는 함수입니다.
넘겨받은 코드는 필요에 따라 적절한 시점에 실행할 것입니다.

콜백함수는 제어권과 관련이 깊습니다. 다른 코드에게 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수입니다.

2. 제어권

2-1. 호출 시점

setInterval을 통해 알아보겠습니다.

var count = 0
var timer = setInterval(funtion(){
	console.log(count)
	if (++count>4){
		clearInterval(timer)
	}
},300)

setInterval을 호출할 때 두개의 매개변수를 전달했는데,
첫번째는 익명 함수이고 두번째는 300이라는 숫자입니다.

setInerval

var intervalID = scope.setInerval(func,delay[,param1,param2...]

scope 에는 window 나 worker 인스턴스가 들어올 수 있습니다. 매개변수로는 func,delay 값을 반드시 전달해야 하며, 세번째 매개변수는 선택입니다.
func 함수는 매 delay 마다 실행됩니다. setInterval를 실행하게 되면, 반복적으로 실행되는 내용 자체를 특정할 수 있는 고유ID가 반환됩니다. 이를 변수에 담는 이유는 중간에 종료할 수 있게 하기 위해서 입니다.

위의 코드를 실행시키면, 0부터 4까지 출력된 이후 종료됩니다.
setInterval 이라고 하는 '다른 코드'에 첫번째 인자로 func 함수를 넘겨주자 제어권을 넘겨받은 setInterval이 스스로의 판단에 따라 이 함수를 실행했습니다. 치처럼 콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가집니다. (제어권 = setInterval )

2-2. 인자

Array.map 메서드를 통해 알아보겠습니다.

map 함수는 어떤 방식으로 동작할까요?
Array.prototype.map(callback[,thisArg])
callback : function(currentValue,index,array)

map 메서드는 첫번째 인자로 callback 함수를 받고, 생략 가능한 두번째 인자로 콜백 함수 내부에서 this로 인식할 대상을 특정할 수 있습니다.
this 를 생략하게 된다면 전역객체가 바이딩됩니다.

콜백 함수의 첫번째 인자에는 현재값이, 두번째는 인덱스가, 세번째는 map 메서드의 대상이 되는 배경 자체가 담깁니다.

콜백 함수를 호출하는 주체가 사용자가 아닌 map 메서드이므로 Map 메서드가 콜백 함수를 호출할 때 인자에 어떤 값들을 순서로 넘길 것인지가 map 메서드에 달렸습니다.

콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수를 호출할 때 인자에 어떤 값들을 어떤 순서로 넘길 것인지에 대한 제어권을 갖습니다.

2-3. this

콜백 함수도 함수이기 때문에 기본적으로 this가 전역객체를 참조하지만, 제어권을 넘겨받을 코드에서 콜백 함수에 별도로 This가 될 대상을 지정한 경우에는 그 대상을 참조하게 됩니다.

map 메서드를 직접 구현해보겠습니다.

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
	}
}

메서드의 구현은 call/apply 메서드에 있습니다.
this에는 thisArg 값이 있을 경우 그 값을 없을 경우는 전역객체를 지정합니다.
제어권을 넘겨받을 코드에서 call/apply 메서드의 첫 번째 인자에 콜백 함수 내부에서의 This가 될 대상을 명시적으로 바인딩하기 때문에 this에 다른 값이 담깁니다.

addEventListner

document.body.innerHTML += '<button id='a'>클릭</button>'
document.body.querySelector('#a')
	.addEventListner('click',function(e){
		console.log(this,e)
	})

addEventListner 는 내부에서 콜백 함수를 호출할 때,
call 메서드의 첫번째 인자에 addEventlistener 메서드의 this를 그대로 넘기도록 정의돼 있기 때문에 콜백 함수 내부에서 this가 addEventListner 를 호출한 주체인 HTML 엘리먼트를 가리키게 됩니다.

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}

[4,5,6].forEach(obj.logValues); // window

콜백 함수로 logValues 메서드를 전달하게 되면 함수만 전달되기 때문에, obj와의 연관이 없어지게 되어 this는 전역객체를 바라보게 됩니다.

4. 콜백 함수 내부의 this에 다른 값 바인딩

위의 문제를 해결하기 위해서 콜백 함수 내부에 this에 원하는 값을 binding 하는 방법을 알아보겠습니다.
전통적으로는 this 대신 다른 변수를 사용하고, 이를 클로저로 만드는 방식을 많이 사용했습니다.

var obj = {
	vals : [1,2,3],
	logValues : function (v,i){
		var self = this;
		return function (){
			console.log(this.vals)
		}
	}
}

이 방식은 실제로 this를 사용하지도 않을 뿐더러 번거롭습니다. 차라리 this 없이 obj 자체로 (obj.vals) 사용하는 것이 낫겟지만 이 방식은 재활용이 불가하다 라는 단점이 있습니다.

bind 메서드를 이용해 위의 방식을 해결할 수 있습니다.

var obj = {
	vals : [1,2,3],
	logValues : function (v,i){
		console.log(this.vals)
	}
}
var obj2 = {vals:[3,4,5]}
setTimeout(obj.logValues.bind(obj2),1500)

5. 콜백 지옥과 비동기 제어

콜백 지옥은 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 깊어지는 현상입니다. 주로 이벤트 처리나 비동기 작업에서 자주 등장합니다.

콜백 함수 안에서 다른 콜백 함수로 이어지는 단계가 깊어진다면 복잡해지고 가독성을 떨어뜨립니다.

이를 해결하는 가장 간단한 방법은 함수를 기명함수로 전환해서 전달하게 되면 코드 자체의 길이가 짧아질 수 있습니다. 하지만 이것 또한 헷갈릴 요지가 있기 때문에 비동기적인 작업도 동기적인 것처럼 처리해주는 기능이 도입되었습니다.
=> Promise, async / await

Promise

Promise 의 인자로 넘겨주는 콜백 함수는 호출할 때 바로 실행되지만, resolve나 reject 함수를 호출하는 구문이 있을 경우, 둘 중 하나가 실행되기 전까지는 then 이나 catch로 넘어가지 않습니다.

또한 async 와 await 를 사용해서도 흡사한 효과를 얻을 수 있습니다.

profile
주경야독

0개의 댓글