파라미터로 함수를 전달하는 함수
콜백함수(Callback Function)란 파라미터로 함수를 전달받아, 함수의 내부에서 실행하는 함수이다.
let number = [1, 2, 3, 4, 5];
number.forEach(x => {
console.log(x * 2);
});
<output>
2
4
6
8
10
콜백함수는 이미 우리의 코드 속에서 자주 사용되고 있다.
예를 들어, forEach
함수의 경우 함수 안에 익명의 함수를 넣어서 forEach
문을 동작시킨다.
let number = [1, 2, 3, 4, 5];
number.forEach(function(x) {
console.log(x * 2);
});
위의 예제를 화살표 함수에서 일반 함수로 바꾼 예제이다.
콜백함수는 이름이 없는 '익명의 함수'를 사용한다. 함수의 내부에서 실행되기 때문에 이름을 붙이지 않아도 된다.
function whatYourName(name, callback) {
console.log('name: ', name);
callback();
}
function finishFunc() {
console.log('finish function');
}
whatYourName('miniddo', finishFunc);
<output>
name: miniddo
finish function
[ TIP ]
JavaScript Data Type
- number, string, boolean, object(function, array, data, regexp), null, undefined
자바스크립트는 null
과 undefined
타입을 제외하고 모든 것을 객체로 다룬다.
함수를 변수 or 다른 함수의 변수처럼 사용할 수 있다. 함수를 콜백함수로 사용할 경우, 함수의 이름만 넘겨주면 된다.
위의 예제에서, 함수를 인자로 사용할 때 callback
, finishFunc
처럼 ()
를 붙일 필요가 없다는 것이다.
let fruit = 'apple'; // Global Variable
function callbackFunc(callback) {
let vegetable = 'tomato'; // Local Variable
callback(vegetable);
}
function eat(vegetable) {
console.log(`fruit: ${fruit} / vegetable: ${vegetable}`);
}
callbackFunc(eat);
<output>
fruit: apple / vegetable: tomato
let userData = {
signUp: '2020-10-06 15:00:00',
id: 'minidoo',
name: 'Not Set',
setName: function(firstName, lastName) {
this.name = firstName + ' ' + lastName;
}
}
function getUserName(firstName, lastName, callback) {
callback(firstName, lastName);
}
getUserName('PARK', 'MINIDDO', userData.setName);
console.log('1: ', userData.name);
console.log('2: ', window.name);
<output>
1: Not Set
2: PARK MINIDDO
우리는 첫 번째 콘솔의 값이 PAKR MINIDDO
이기를 기대했지만, Not Set
이 출력된다.
setName()
함수가 실행되기 전의 name 값이 나오는 것인데, 이는 getUserName()
이 전역 함수이기 때문이다.
즉, setName()에서 사용된 this
객체가 window
라는 글로벌 객체를 가리킨다.
따라서 this
를 보호할 수 있도록 콜백함수를 만들어야 한다.
해결 방안 :
call()
과apply()
를 사용하여this
를 보호할 수 있다.
call()
: 첫 번째 인자로 this
객체 사용, 나머지 인자들은 ,
로 구분apply()
: 첫 번째 인자로 this
객체 사용, 나머지 인자들은 배열 형태로 전달// call
...
function getUserName(firstName, lastName, callback, obj) {
callback.call(obj, firstName, lastName); - (1)
}
getUserName('PARK', 'MINIDDO', userData.setName, userData); - (2)
console.log(userData.name);
<output>
PARK MINIDDO
( 2 ) 에서 마지막 인자에 담긴 userData
는 ( 1 )에서 call 함수의 첫번째 인자로 전달된다.
즉, call()
에 의해서 userData
에 this
객체가 매핑된다.
apply()
도 인자를 배열로 전달한다는 점만 다르고 동일하게 작동한다.
// apply
...
function getUserName(firstName, lastName, callback, obj) {
callback.apply(obj, [firstName, lastName]);
}
getUserName('PARK', 'MINIDDO', userData.setName, userData);
console.log(userData.name);
<output>
PARK MINIDDO
비동기 호출이 자주 일어나는 프로그램의 경우 '콜백 지옥'이 발생한다.
함수의 매개변수로 넘겨지는 콜백 함수가 반복되어 코드의 들여쓰기 수준이 감당하기 힘들어질 정도로 깊어지는 현상이다.
function add(x, callback) {
let sum = x + x;
console.log(sum);
callback(sum);
}
add(2, function(result) {
add(result, function(result) {
add(result, function(result) {
console.log('finish!!');
})
})
})
<output>
4
8
16
finish!!
해결 방안 :
Promise
를 사용하여 콜백지옥을 탈출할 수 있다.
function add(x) {
return new Promise((resolve, reject) => {
let sum = x + x;
console.log(sum);
resolve(sum);
})
}
add(2).then(result => {
add(result).then(result => {
add(result).then(result => {
console.log('finish!!');
})
})
})
<output>
4
8
16
finish!!
Promise
는 정상 수행 후 resolve
, 실패 후 reject
가 실행된다.
callback
을 사용했던 것과 마찬가지로 resolve
에 값을 담아 전달한다.
하지만, 이 패턴도 그리 좋은 방법은 아니다. 결국 콜백지옥처럼 들여쓰기 수준을 감당하기 힘들어진다.
해결 방안 :
Promise
의 return 사용하여 Promise Hell을 탈출할 수 있다.
프로미스를 사용하면 비동기 메서드에서 마치 동기 메서드처럼 값을 반환할 수 있습니다. 다만 최종 결과를 반환하지는 않고, 대신 프로미스를 반환해서 미래의 어떤 시점에 결과를 제공합니다.
MDN
에서 정의하고 있는 Promise
에 대한 설명이다.
프로미스는 비동기 호출 시, 마치 동기 호출 처럼 값을 반환할 수 있다는 문구에 집중해보자.
즉, resolve
를 통해 전달 받은 값을 반환하여 사용해야 한다!
function add(x) {
return new Promise((resolve, reject) => {
let sum = x + x;
console.log(sum);
resolve(sum);
})
}
add(2).then(result => {
return add(result);
}).then(result => {
return add(result);
}).then(result => {
console.log('finish!!');
})
<output>
4
8
16
finish!!
자바스크립트 스터디를 시작하며, '콜백함수' 강의를 맡게 되었다.
정의만 알았을 뿐 이렇게 자세히 들여다 본 적은 없었는데 차근차근 따라가니 어느정도 이해 할 수 있었다.
아마도 콜백 함수의 결론은 "Promise와 async/await를 사용하자"가 아닐까?
forEach, map과 같이 간단하게 사용할 수 있는 함수를 제외하고 들여쓰기가 많아지는(복잡해지는) 함수는 최대한 피해야할 것 같다. 비동기와 동기에 대한 개념도 쓸 때마다 헷깔리는데 이렇게 한 번 정리해야 할 것 같다 :-)
https://yubylab.tistory.com/entry/자바스크립트의-콜백함수-이해하기
https://wooooooak.github.io/javascript/2018/12/08/call,apply,bind/
❤ 누르려고 회원가입 했습니다!!!
너무 좋은 글 감사합니다!!!
https://cookie-clicker.co/