콜백

비동기식은 언제 실행이 될지, 또 언제 실행이 완료 될지 등을 알 수가 없다. 이를 위해서 callback을 인자로 받아서 특정 시점에(나중에) 실행해주겠다를 약속받을 수 있다. 이렇게 작성하는 것을 콜백 기반 비동기 프로그래밍이라고 한다.

콜백 속 콜백

비동기 로직을 실행한 이후 콜백으로 콜백을 받을 수 있는 비동기 로직을 넣을 수 있다. 이를 무한히 반복하게 되면.. ☠️

// 함수 정의
function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;
  script.onload = () => callback(script);
  document.head.append(script);
}

loadScript('/my/script.js', function(script) {

  loadScript('/my/script2.js', function(script) {

    loadScript('/my/script3.js', function(script) {
      // 세 스크립트 로딩이 끝난 후 실행됨
    });

  })

});

에러 핸들링

에러가 발생했을 때의 동작을 외부에서 정의할 수 있도록 error값을 넣어줄 수도 있다.

function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(null, script);
  script.onerror = () => callback(new Error(`${src}를 불러오는 도중에 에러가 발생했습니다.`));

  document.head.append(script);
}

loadScript('/my/script.js', function(error, script) {
  if (error) {
    // 에러 처리
  } else {
    // 스크립트 로딩이 성공적으로 끝남
  }
});

이를 '오류 우선 콜백(error-first callback)'이라고 한다.

멸망의 피라미드

콜백 기반 비동이 처리를 무한히 반복하는 경우 콜백 지옥(callback hell)을 경험할 수 있다.

loadScript('1.js', function(error, script) {

  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('2.js', function(error, script) {
      if (error) {
        handleError(error);
      } else {
        // ...
        loadScript('3.js', function(error, script) {
          if (error) {
            handleError(error);
          } else {
            // 모든 스크립트가 로딩된 후, 실행 흐름이 이어집니다. (*)
          }
        });

      }
    })
  }
});

이는 각각의 동작을 분리하고 함수로 만들면 이를 피할 수는 있다. 다만 함수가 여기저기 있다면 가독성에 치명적..

loadScript('1.js', step1);

function step1(error, script) {
  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('2.js', step2);
  }
}

function step2(error, script) {
  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('3.js', step3);
  }
}

function step3(error, script) {
  if (error) {
    handleError(error);
  } else {
    // 모든 스크립트가 로딩되면 다른 동작을 수행합니다. (*)
  }
};

우리는 이를 해결하기 위해서 프로미스를 사용할것


프라미스