코어자바스크립트 - 콜백함수

Hwang Tae Young·2023년 3월 17일
0

✅ 콜백함수란?

  • 다른 코드의 인자로 넘겨주는 함수이며, 넘겨받은 코드는 이 콜백 함수필요에 따라 적절한 시점에 실행 시킬 수 있습니다.
  • 콜백 함수는 다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로써 그 제어권도 함꼐 위임한 함수입니다.
  • 위임받은 코드는 자체적인 내부 로직에 의해 콜백함수를 적절한 시점에 실행 합니다.

✅ 제어권

  • 말 그대로 함수를 실행 시킬 수 있는 제어권을 준 것이다.
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초)
// 2  (0.9초)
// 3  (1.2초)
// 4  (1.5초)
code 호출 주체 제어권
cbFunc(); 사용자 사용자
setInterval(cbFunc, 300); setInterval setInterval
  • 처음 cbFunc()을 실행할때는 사용자 즉 우리가 실행을 시킨 것이지만 setInterval에서는 인자로서 콜백함수가 되며,
    setInterval 에게 제어권을 넘겨주며 적절한 시점에 (0.3초마다 한번씩) cbFunc()을 실행시키는 것이다.

✅ 인자

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]

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]
  • map 메서드 - arr.map(callback(currentValue[, index[, array]])[, thisArg])

    • 메서드의 대상이 되는 배열의 모든 요소들을 처음부터 순서대로 꺼내어 콜백 함수를 반복 호출하고,
      콜백함수의 실행 결과값을들 모아 새로운 배열을 만들어준다.
    • 콜백함수의 첫번쨰 인자 => 대상이 되는 배열중 현재 값
    • 두번째 인자 => 현재 값의 인덱스
    • 세번째 인자 => map 메서드의 대상읻 ㅚ는 배열 그 자체
  • map 메서드를 호출해서 원하는 배열을 얻으려면 map 메서드에 정의된 규칙에 따라 함수를 작성해야 한다.
    (newArr1, newArr2의 출력 결과를 비교해보면 출력 값이 다른것을 알 수 있다.)

  • map 메서드에 정의된 규칙에는 콜백 함수의 인자로 넘어올 값들 및 그 순서도 포함돼 있다.

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


✅ this

  • 콜백 함수도 함수이기 때문에 기본적으로 this가 전역객체를 참조하지만,
    제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조합니다.
setTimeout(function() {
  console.log(this);
}, 300); // (1) Window { ... }

[1, 2, 3, 4, 5].forEach(function(x) {
  console.log(this); // (2) Window { ... }
});

document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener(
  'click',
  function(e) {
    console.log(this, e); // (3) <button id="a">클릭</button>
  } // MouseEvent { isTrusted: true, ... }
);
  • 함수 선언문으로 작성된 경우 this를 명시적으로 바인딩 하지 않는다면 window객체를 가리킨다.

  • addEventListerner은 document.body.querySelector('#a')가 호출 객체이기 때문에 버튼을 가리킨다

    • 하지만 화살표 함수로 했을 경우에는, this 바인딩을 하지 않기 때문에 window객체를 가리킨다.

✅ 콜백 함수 내부의 this에 다른 값 바인딩하기

var obj1 = {
  name: "obj1",
  func: function () {
    console.log(this.name);
  },
};

setTimeout(obj1.func.bind(obj1), 1000);

var obj2 = { name: "obj2" };
setTimeout(obj1.func.bind(obj2), 1500);
  • 위와같은 방법으로 사용 가능하다!

✅ 콜백 지옥과 비동기 제어

  • 콜백 지옥이란?
    • 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 너무 감당하기 힘들 정도로 깊어지는 현상
  • 동기(synchronous)
    • 기본적으로 코드들이 동작하는 방식이며, 현재 동작하고 있는 것이 끝나야 다음으로 넘어가는 방식
  • 비동기(asynchronous)
    • 동기와 반대로, 앞전 코드의 완료 여부와 관계없이 다음으로 넘어가는 방식

콜백 지옥 예제

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

  • Promise + 클로저 표현
var addCoffee = function (name) {
  return function (prevName) {
    return new Promise(function (resolve) {
      setTimeout(function () {
        var newName = prevName ? `${(prevName, name)}` : name;
        console.log(newName);
        resolve(newName);
      }, 500);
    });
  };
};

addCoffee("에스프레소")()
  .then(addCoffee("아메리카노"))
  .then(addCoffee("카페모카"))
  .then(addCoffee("카페라떼"));

  • 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();

참고 : 코어자바스크립트

profile
더 나은 개발자가 되기 위해...☆

0개의 댓글