Home

[JS] 코어 자바스크립트 Chapter.4 콜백함수

코어 자바스크립트

코어 자바스크립트

정재남 지음

위키북스

목차

콜백 함수

  • 콜백 함수(callback function)은 다른 코드의 인자로 넘겨주는 함수를 맗한다.
  • “어떤 함수 A를 호출하면서 특정 조건일때 함수 B를 수행하라라는 요청”을 같이 보내는 것이라 생각하면 된다.
  • 콜백 함수는 다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로서 해당 함수의 제어권을 다른 코드에 넘겨준것이다. 이 콜백 함수를 넘겨받은 함수는 코드에 따라서 적절한 로직, 시점에 함수를 실행하는 것이다.

제어권

setTimeout(function(){ alert("Hello"); }, 3000);

// setTimeout은 첫번째 인자로 함수를 넘겨 받는다.
// 이걸 바로 콜백 함수라 생각할 수 있다.
// 해당 코드는 3000ms를 기다린 후 Hello를 출력한다.
  • 제어권은 함수를 어떤 시점, 어떤 인자를 넘길것인지를 결정하는 것이라 생각하자.
  • setTimeout()을 호출하는 것은 사용자이다. 하지만 setTimeout()에 넘겨준 함수를 호출하는 것은 setTimeout()이 되는 것이며 제어권 또한 setTimeout()이 가지고 있는것이다.
  • 콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수를 호출할 때 인자에 어떤 값들을 어떤 순서로 넘길것인지를 제어하는 제어권을 갖고 있는 것이다.
  • 콜백 함수도 결국은 함수이기 때문에 기본적으로 this에는 전역객체를 참조한다. 다만 콜백 함수에 별도의 this가 될 대상을 넘기면 콜백 함수는 해당 값을 참조 한다.

콜백 함수는 함수다

  • 콜백 함수로 어떤 객체의 메서드를 전달하더라도 그 메서드는 메서드가 아닌 함수로서 호출 된다.
var obj = {
  a: 1,
  b: 2,
  test: function() {
    console.log(this);
  }
}

obj.test() // { a: 1, b: 2, test: ...}  Object
[1, 2].forEach(obj.test);  // 전역객체

콜백 함수 내부의 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 메서드를 사용하면 코드가 훨씬 간결해진다.

콜백 지옥과 비동기 제어

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

Promise

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(true);
  }, 1000);
}).then((isOk) => {
  setTimeout(() => {
    resolve(isOk);
  }, 1000);
}).catch((err) => {
  console.error("ERROR: ", err);
});
  • ES6에서 추가되었다.
  • new 연산자와 함께 쓰이며, 내부에서 resolve, reject 함수를 호출하는 구문이 있을 경우 둘 중 하나가 실행되기 전까지는 다음(then)이나 오류(catch)구문으로 넘가지 않는다.
  • 일반적으로 비동기 작업이 완료 될때 resolve, reject를 호출해서 다음 작업을 수행한다.

Generator

  • 함수 뒤에 *를 이용하여 구현한다.
  • Generator 함수를 실행하면 Iterator가 반환되며, Iterator는 next라는 메소드를 가지고 있다. 이 next 메소드를 호출하면 Generator 함수 내부에서 가장 먼저 등장하는 yield에서 함수의 실행을 잠시 멈춘다. 이후 다시 next 메서드를 호출하면 앞서 멈췄던 부분부터 시작해서 그다음에 등장하는 yield에서 함수의 실행을 멈춘다.
  • 비동기 작업이 완료되는 시점마다 next 메서드를 호출한다면 코드는 동기적으로 실행될 것이다.

async/await

  • 비동기 작업을 수행하고자 하는 함수 앞에 async를 표기하고, 함수 내부에서 실질적으로 비동기 작업이 필요한 위치마다 await를 표기하는 것으로 뒤의 내용을 Promise로 자동 전환하고, 해당 내용이 resolve된 이후에야 다음으로 진행된다.
  • Promise의 then과 흡사한 효과를 얻을 수 있다.

정리

  • 콜백 함수는 다른 코드에 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수를 말하며 제어권은 넘겨 받은 코드는 콜백 함수를 언제 실행할 것인지, 어떤 값을 넘겨줄 것인지를 결정한다.
  • 어떤 함수에 인자로 메서드를 전달 하더라도 이는 결국 함수로서 실행된다.
Loading script...