콜백 함수는 다른 함수의 전달인자로 넘겨주는 함수이며 나중에 호출되는 함수를 의미한다.
아래 코드처럼 인수로 전달된 함수(콜백 함수)는 원하는 동작이 완료되었을 때 실행된다. 이런 방식을 콜백 기반 비동기 프로그래밍이라고 한다.
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(script);
document.head.append(script);
}
loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => {
alert(`${script.src}가 로드되었습니다.`);
alert( _ ); // 스크립트에 정의된 함수
});
💡 다른 예시
// callback in action : 반복 실행하는 함수(iterator) [1,2,3].map(function(el, idx){ return el * el; }) // callback in action : 이벤트에 따른 함수(event handler) document.querySelector("#btn").addEventListner("click", function(e){ console.log("click!"); })
🚨 주의
호출된 함수를 연결하는 것이 아니라 함수 자체를 연결한다.// ⭕ document.querySelector("#btn").onclick = handleClick; document.querySelector("#btn").onclick = function(){ handleClick(); } document.querySelector("#btn").onclick = handleClick.bind(); // ❌ document.querySelector("#btn").onclick = handleClick();
아래 코드처럼 에러를 처리하는 방식을 오류 우선 콜백(error-first callback)이라고 한다.
오류 우선 콜백의 첫번째 인자를 이용해 에러가 발생했을 때 callback(err)을 호출한다. 두번째 인자를 이용해 원하는 동작이 성공한 경우에 callback(null, result)를 호출한다.
loadScript('/my/script.js', function(error, script) {
if (error) {
// 에러 처리
} else {
// 스크립트 로딩이 성공적으로 끝남
}
});
콜백 기반의 비동기 처리는 비동기에서 순서를 제어할 수 있지만 꼬리에 꼬리를 무는 비동기 동작이 많아지면 아래 코드처럼 콜백 지옥에 빠지게 된다.
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 {
// 모든 스크립트가 로딩되면 다른 동작을 수행한다.
}
};