[코어 자바스크립트] 4장 콜백함수

Dochaki·2024년 10월 5일

코어자바스크립트

목록 보기
4/7
post-thumbnail

4-1 콜백 함수란?

  • 콜백함수는 다른 코드의 인자로 넘겨주는 함수
  • 인자를 넘겨줌으로써 제어권도 함께 위임 -> 위임받은 코드는 자체적인 내부 로직에 의해 콜백함수를 적절한 시점에 실행

4-2 제어권

4-2-1 호출 시점

  • 제어권을 넘겨받은 함수는 그 콜백 함수의 호출 시점에 대한 제어권도 가짐
  • setInterval함수는 window, worker 객체를 가지고 있음
    • 인자로는 func와 delay가 필수임
    • func로 넘겨준 함수는 매 delay마다 실행하게 되고 특정 ID를 부여해서 반환

4-2-2 인자

  • 콜백함수를 넘겨받을 함수를 정의할 때 콜백 함수의 인자를 넘겨받을 함수를 지정할 수 있음
  • map 메서드는 첫 번째 인자로 콜백 함수를 받고 선택 사항인 두 번째 인자로 콜백 함수 내부에서 this로 인식할 대상을 특정함
    • thisArg를 생략할 경우 전역객체 바인딩
  • jquery에서는 index, value의 순서가 보통이지만, map 등의 함수에서는 value, index의 순서로 인자가 감
  • 그래서 콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수를 호출할 때 인자에 어떤 값들을 어떤 순서로 넘길 것인지에 대한 제어권을 가짐

4-2-3 this

  • 콜백함수도 함수라서 기본적으로 this는 전역객체를 참조하지만, 제어권을 넘겨받을 코드에서 콜백함수에 this를 직접 지정한 경우 그 대상을 참조함.
  • map함수 내부에서는 this를 map함수를 호출하는 array로 명시적으로 바인딩
  • foreach함수는 별도의 인자로 this를 받아야지만 this가 호출한 배열을 가리킴
  • addEventListener는 내부에서 그 이벤트를 호출한 html 엘레멘트를 가리킴

4-3 콜백 함수는 함수다

  • 콜백함수로 어떤 객체의 메서드를 전달하더라도 그 메서드는 메서드가 아닌 함수로서 호출됨
  • var obj = {
      vals: [1, 2, 3],
      logValues: function (v, i) {
        console.log(this, v, i);
      },
    };
    obj.logValues(1, 2); // {vals: [1, 2, 3], logValues: f} 1 2
    [4, 5, 6].forEach(obj.logValues); // Window {...} 4 0
    // Window {...} 5 1
    // Window {...} 6 2
  • logValues 메서드를 forEach 함수의 콜백 함수로서 전달함.
  • obj를 this로 하는 메서드를 그대로 전달한 것이 아니라, obj.logValues가 가리키는 함수만 전달 --> 메서드로서 호출할 때가 아닌 한 obj와 직접적인 연관이 없어짐

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

  • 객체의 메서드를 콜백 함수로 전달하면 해당 객체를 this로 바라볼 수 없게 됨

  • ES5 이전에는 this를 다른 변수에 담아 콜백 함수로 활용할 함수에 this대신 그 변수를 사용하게 하고 클로저로 만들었음.

    • var obj1 = {
        name: "obj1",
        func: function () {
          var self = this;
          return function () {
            console.log(self.name);
          };
        },
      };
      var callback = obj1.func();
      setTimeout(callback, 1000);
    • 이건 실제 this를 사용하지 않음. this 쓸 필요 없음
  • ES5 이후에는 bind 메서드를 이용함

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

4-5 콜백 지옥과 비동기 제어

  • 콜백 지옥 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상

  • 동기: 현재 실행 중인 코드가 완료된 후에야 다음 코드를 실행하는 방식

    • CPU의 계산에 의해 즉시 처리가 가능한 대부분의 코드는 동기적인 코드
    • 계산식이 복잡해서 CPU가 계산하는 데 시간이 많이 필요한 경우도 동기적인 코드
  • 비동기: 현재 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어감

    • 사용자의 요청에 의해 특정 시간이 경과되기 전까지 어떤 함수의 실행을 보류한다거나(setTimeout), 사용자의 직접적인 개입이 있을 때 비로소 어떤 함수를 실행하도록 대기한다거나(addEventListener), 웹브라우저 자체가 아닌 별도의 대상에 무언가를 요청하고 그에 대한 응답이 왔을 때 비로소 어떤 함수를 실행하도록 대기하는 등(XMLHttpRequest), 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 비동기적인 코드
  • 콜백 지옥 해결

    • 기명함수로 변환

      • 가독성 문제와 어색함을 동시에 해결하는 가장 간단한 방법 -> 근데 일회성 함수를 전부 변수에 할당하면 비효울적임
    • 비동기 작업의 동기적 표현

      • 1. Promise

        • ES6 promise는 .then의 방식으로 연쇄적으로 콜백함수를 호출함으로서 콜백지옥을 해결
        • 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("카페모카"));
          • 3번째 줄에 클로저가 등장
            • 클로저는 주변 상태(어휘적 환경)에 대한 참조와 함께 묶인(포함된) 함수의 조합
            • 즉, 클로저는 내부 함수에서 외부 함수의 범위에 대한 접근을 제공
            • JavaScript에서 클로저는 함수 생성 시 함수가 생성될 때마다 생성됨
      • 2. Generator

        • ES6 Generator는 function에 * 을 붙여서 정의하면 generator함수가 됨

        • yield로 실행을 멈추는 곳을 지정할 수 있음

        • 실행을하면 iterator객체가 반환되며, .next()를 호출하면 멈췄던 곳부터 다시 시작

        • 비동기 작업이 완료되는 시점마다 next 메서드를 호출한다면 Generator 함수 내부의 소스가 위에서 아래로 순차적으로 실행됨

        • var addCoffee = function (prevName, name) {
            setTimeout(function () {
              coffeeMaker.next(prevName ? prevName + ", " + name : name);
            }, 500);
          };
          
          var coffeeGenerator = function* () {
            var espresso = yield addCoffee("", "에스프레소");
            var americano = yield addCoffee(espresso, "아메리카노");
            var mocha = yield addCoffee(americano, "카페모카");
          };
          var coffeeMaker = coffeeGenerator();
          coffeeMaker.next();
      • async/await

        • ES2017 async/await 가독성이 뛰어나고 작성법도 간단 -> (프로젝트하면서 개인적으로 이것만 쓴 것 같음)

        • 비동기 작업을 수행하려는 함수 앞에 async를 표기하고 함수 내부의 비동기 작업이 필요한 위치마다 await를 표기

        • 이렇게 해서 뒤의 내용들이 Promise로 전환되고 해당 내용이 resolve된 이후 다음 작업이 진행됨. Promise의 then과 흡사한 효과를 볼 수 있음

        • 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("에스프레소");
            await _addCoffee("아메리카노");
            await _addCoffee("카페모카");
          };
          coffeeMaker();

4-6 정리

  • 콜백 함수는 다른 코드에 인자로 할당됨과 동시에 그 제어권도 함께 위임한 함수.
  • 제어권
    • 콜백 함수를 호출하는 시점
    • 콜백 함수를 호출할 때 인자로 넘겨줄 값들 및 순서가 있음
    • 콜백 함수의 this가 뭘 보게 할지 정해져 있는 경우 있음. 정해져 있지 않으면 전역 객체. 사용자 임의로 바꾸고 싶으면bind 메서드
    • 어떤 함수에 인자로 메서드 전달해도 결국 함수
    • 콜백 지옥을 해결하기 위한 방법으로는 Promise, Generator, async/await

0개의 댓글