callback

양세희·2022년 6월 15일
0

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('sehee', finishFunc);

// output
// name: sehee
// finish function      

자바스크립트는 nullundefined 타입을 제외하고 모든 것을 객체로 다룬다.

함수를 변수 또는 다른 함수의 변수처럼 사용할 수 있다.

함수를 콜백함수로 사용할 경우, 함수의 이름만 넘겨주면 된다.

즉, 위의 예제에서 함수를 인자로 사용할 때,
callback, finishFunc처럼 ()를 붙일 필요가 없다.

전역변수, 지역변수 콜백함수의 파라미터로 전달 가능

  • 전역변수(Global variable) : 함수 외부에서 선언한 변수
  • 지역변수(Local variable) : 함수 내부에서 선언한 변수
let fruit = 'apple'; // Global variable

function callbackFunc(callback) {
  let vegetable = 'carrot'; // Local variable
  callback(vegetable);
}

function eat(vegetable) {
  console.log(`fruit: ${fruit} / vegetable: ${vegetable}`);
}

callbackFunc(eat);

// output
// fruit: apple / vegetable: carrot

콜백함수 주의할 점

this를 사용한 콜백함수

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() 에 의해서 userDatathis 객체가 매핑된다.

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

콜백지옥 (Callback Hell)

비동기 호출이 자주 일어나는 프로그램의 경우 '콜백 지옥'이 발생한다.

함수의 매개변수로 넘겨지는 콜백 함수가 반복되어 코드의 들여쓰기 수준이
감당하기 힘들어질 정도로 깊어지는 현상이다.

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에 값을 담아 전달한다.

하지만, 이 패턴도 그리 좋은 방법은 아니다.
결국 콜백지옥처럼 들여쓰기 수준을 감당하기 힘들어진다.

해결 방안 : Promisereturn을 사용하여 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!!

0개의 댓글