Callback 함수
한 줄 정의 : 다른 함수에 인자로 전달되어 특정 작업이 완료된 후 호출되는 함수
■ 콜백함수의 작동 원리
1. 함수 호출 : 비동기 작업을 수행하는 함수가 호출됨, 이 함수는 콜백 함수를 인자로 받음.
2. 비동기 작업 수행 : 함수는 비동기 작업(예: 타이머 설정, 네트워크 요청 등)을 시작함.
3. 태스크 큐에 콜백 함수 추가 : 비동기 작업이 완료되면 콜백 함수가 태스크 큐에 추가됨.
4. 이벤트 루프 : 호출 스택이 비어 있으면 이벤트 루프가 태스크 큐에서 콜백 함수를 꺼내 호출 스택에 추가하고 실행함.
예제 :
let button = document.getElementById("button");
button.addEventListener("click", function () { // 콜백 함수
console.log("clicked!");
});
왜 콜백함수? :
"비동기처리를 할 때 콜백함수를 사용해서 처리 할 수 있다." 로 주로 들어본 것 같다. setTimeout을 이용해 2초 후에 비동기 작업이 실행됬는지 확인하는 코드를 만들어보자.
// 비동기 작업을 수행하는 함수
function doAsyncTask(callback) {
console.log('비동기 작업 시작...');
// 2초 후에 콜백 함수 호출
setTimeout(() => {
console.log('비동기 작업 완료!');
callback(null, '작업 결과 데이터');
}, 2000);
}
// 콜백 함수
function handleTaskCompletion(err, result) {
if (err) {
console.error('작업 중 오류 발생:', err);
return;
}
console.log('콜백 함수 호출됨: ', result);
}
// 비동기 함수 호출
doAsyncTask(handleTaskCompletion);
위와 같이 만들 수 있을 것이다. 하지만 아래와 같은 '콜백 지옥' 과 같은 상황이 펼쳐질 수 있다.
// 첫 번째 비동기 작업
function doFirstTask(callback) {
console.log('첫 번째 작업 시작...');
setTimeout(() => {
console.log('첫 번째 작업 완료!');
callback(null, '첫 번째 작업 결과');
}, 1000);
}
// 두 번째 비동기 작업
function doSecondTask(data, callback) {
console.log('두 번째 작업 시작...', data);
setTimeout(() => {
console.log('두 번째 작업 완료!');
callback(null, '두 번째 작업 결과');
}, 1000);
}
// 세 번째 비동기 작업
function doThirdTask(data, callback) {
console.log('세 번째 작업 시작...', data);
setTimeout(() => {
console.log('세 번째 작업 완료!');
callback(null, '세 번째 작업 결과');
}, 1000);
}
// 콜백 지옥 예시
doFirstTask((err, result1) => {
if (err) {
console.error('첫 번째 작업 오류:', err);
return;
}
doSecondTask(result1, (err, result2) => {
if (err) {
console.error('두 번째 작업 오류:', err);
return;
}
doThirdTask(result2, (err, result3) => {
if (err) {
console.error('세 번째 작업 오류:', err);
return;
}
console.log('모든 작업 완료:', result3);
});
});
});
Promise
이런 콜백 지옥을 보완하기 위해 필요한 것은 Promise 이다.
Promise 자바스크립트 비동기 처리에 사용되는 객체로 일반적으로 데이터에 대한 응답을 받아올 때 사용되어 진다.
데이터를 요청하고 받아오는 과정에서, 데이터를 아직 받지 않았는데, 화면에 표시하려고 하면 문제가 생긴다. 이를 방지하기 위해 Promise를 사용하여 응답을 받을 때 까지 기다린 후 실행이 되어진다.
<script>
function countdown(seconds) {
return new Promise((function(resolve, reject){
for(let i = seconds ;i >= 0 ; i--){
setTimeout(function(){
if(i===13) return reject(new Error("Oh my god"));
if(i > 0) console.log(i + '...');
else resolve(console.log("GO!"));
}, (seconds - i) * 1000)
}
}))
}
countdown(3)
.then(() =>console.log('데이터를 받은 후 실행'))
</script>
Promise의 상태는 총 3가지가 있는데, 아래와 같다.
Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
Promise는 then, catch, finally와 같은 메서드를 통하여 적절한 작업을 수행할 수 있게 해준다 . 이런 메서드를 사용하여 여러 비동기 작업을 순차적으로 연결하는 것을 '프로미스 체이닝'이라고 한다.
Async/Await
Promise를 기반으로 비동기 코드를 더 직관적이고 동기적인 방식으로 작성할 수 있게 해주는 문법이다. async/await를 사용하는 이유는 Promise는 여러개의 비동기 작업을 순차적으로 실행할 때 복잡해지고 문제가 생길 여지가 있기 때문이다. 'async' 키워드를 함수 앞에 붙이면 해당 함수는 항상 'Promise"를 반환하게 된다. 'await' 키워드는 'Promise'가 처리될 떄까지 기다렸다고 결과를 반환한다.
async/await는 Promise와 달리 try~catch문을 통해 에러처리를 해줘야 한다.
// Promise 체이닝 예시
fetchData1()
.then(result1 => {
return fetchData2(result1);
})
.then(result2 => {
return fetchData3(result2);
})
.then(result3 => {
console.log(result3);
})
.catch(error => {
console.error(error);
});
// async/await 예시
async function fetchAllData() {
try {
const result1 = await fetchData1();
const result2 = await fetchData2(result1);
const result3 = await fetchData3(result2);
console.log(result3);
} catch (error) {
console.error(error);
}
}
fetchAllData();
'개발 > STUDY' 카테고리의 다른 글
브라우저 동작 방법 (0) | 2024.05.23 |
---|---|
프로토타입 (0) | 2024.05.23 |
렉시컬스코프와 클로저 (0) | 2024.05.22 |
useState를 사용할 때는 왜 const를 쓰는가? (0) | 2024.05.22 |
var와 let 그리고 호이스팅 (0) | 2024.05.22 |