코어자바스크립트 4장<콜백함수>

김정현·2021년 1월 17일
1
post-thumbnail

1. 콜백 함수란?

되돌아 호출해달라는 뜻으로 다른 코드의 인자로 넘겨주는 함수 이며, 그 제어권도 함께 위임한 함수 이다.

2. 제어권


2-1) 호출시점

1) 콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가진다.

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

2-2) 인자

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

Array.prototype.map(callback[, thisArg])
callback: function(currentValue, index, array)

2-3) this

3) 콜백 함수의 this가 무엇을 바라보게 할지 정해진 경우도 있는데, 정하지 않은 경우에는 전역객체를
바라본다. 사용자가 임의로 this를 바꾸고 싶은 경우 bind 메소드를 사용한다

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);
    mapperArr[i] = mappedValue;
  }
  return mappedArr;
}

3. 콜백 함수는 함수다

var obj = {
        vals: [1,2,3],
        logValues: function(v,i) {
                console.log(this, v, i);
        }
};
obj.logValues(1,2);                 // (1)
[4,5,6].forEach(obj.logValues);     // (2)

(1)의 결과는 obj를 가리키고, (2)의 결과는 전역객체를 가리킨다. logValues는 메서드이지만 (2)에서 obj를 this로 하는 메서드를 그대로 전달한 것이 아니라 함수만 전달한 것이다.

결국 어떤 함수의 인자에 객체의 메서드를 전달하더라도 이는 결국 메서드가 아닌 함수일 뿐이다. 이 차이를 정확히 이해하는 것이 중요하다.

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

  • 객체의 메서드를 콜백 함수로 전달하더라도 이는 함수로 호출되기 때문에 해당 객체를 this로 바라볼 수 없다.
  • 위 문제를 해결하기 위해서 this를 다른 변수에 담아서 콜백 함수로 활용할 함수에 this 대신 그 변수를 사용하고, 이를 클로저로 만드는 방식이 많이 사용 되었다.
var obj = {
  a: 1,
  b: 2,
  test: function() {
    var self = this;
    return function() {
      console.log(self);
    }
  },
};

var callback = obj.test();
setTimeout(callback, 1000); // 1초뒤에 { a: 1, b: 2, test: ... } Object가 출력된다.

하지만 이방식은 불편하기 그지 없다. 게다가 코드도 복잡하다. 이런 방법이 있다고만 기억하고
다른 방법을 사용하자.

var obj = {
  a: 1,
  b: 2,
  test: function() {
    console.log(this);
  },
};

setTimeout(obj.test.bind(obj), 1000); // 1초뒤에 { a: 1, b: 2, test: ... } Object가 출력된다.

bind 메서드를 사용하면 코드가 훨씬 간결해진다.

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

콜백 지옥은 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기가 중복해서 엄청나게 많아지는 것을 말한다.

setTimeout(() => {
  setTimeout(() => {
    setTimeout(() => {
      // 이런식으로 콜백함수 안에 다시 콜백함수가 들어가면
      // 들여쓰기가 계속 늘어나버린다.
    }, 1000)
  }, 1000)
}, 1000);
  • 동기(synchronous)적인 코드는 현재 실행 중인 코드가 완료된 후에야 다음 코드를 실행하는 코드를 말한다.
  • 비동기(asynchronous)적인 코드는 현재 실행 중인 코드의 완료 여부와 무관하게 즉시 다음코드로 넘어 간다.
  • CPU 계산산에 의해서 즉시 처리가 가능한 코드는 일반적으로 동기적인 코드이며, 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 비동기적인 코드라 볼 수 있다.
  • ES6에서는 PromiseGenerator 등이 도입되었고, ES2018에서는 async/await가 도입되었다.

콜백지옥 해결 방법

1. 기명 함수로 변환 : 코드 가독성을 높일 수 있다.
2. Promise : new 연산자와 함께 호출한 Promise의 인자로 넘겨주는 콜백 함수는 호출할 때 바로 실행되지만
내부에 resolve 또는 reject 함수를 호출하는 구문이 있을 경우 둘 중 하나가 실행되기 전까지는 then 또는 catch로 넘어가지 않는다.
3. Generator : next 메서드를 호출하면 Generator 함수 내부에서 가장 먼저 등장하는 yield에서 함수의 실행을 멈춘다. 비동기 작업이 완료되는 시점마다 next를 호출하면 내부의 소스를 위에서 아래로 순차적으로
할 수 있다.
4. async/await : 비동기 작업을 수행하고자 하는 함수 앞에 async를 표기하고 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 표기하는 것 만으로도 뒤의 내용을 Promise로 자동 전환하고
해당 내용이 resolve된 이후에 다음으로 넘어간다.

0개의 댓글