JavaScript는 싱글 스레드 언어로, 일반적으로 한 번에 하나의 작업만 처리할 수 있습니다. 시간이 많이 걸리는 작업을 동기적으로 처리하면, 해당 작업이 종료될 때까지 프로그램의 다른 부분이 블로킹(작업 중단)됩니다. 하지만, 비동기 처리를 통해 이러한 블로킹을 방지하고 다른 작업을 병렬적으로 진행할 수 있습니다.
JavaScript에서 비동기 처리가 가능한 타이머 함수(setTimeout, setInterval), HTTP 요청, 그리고 이벤트 핸들러 등이 있습니다. 이런 비동기 작업들은 브라우저나 Node.js의 이벤트 루프에 의해 관리되며, 이벤트 루프는 호출 스택이 비어 있을 때 작업 큐(Task Queue) 또는 마이크로태스크 큐(Microtask Queue)에서 작업을 하나씩 꺼내 처리함으로써 비동기 실행을 가능하게 합니다.
비동기 작업은 병렬로 처리되는 것처럼 보이지만, 실제로는 비동기 작업들이 완료될 때까지 다른 작업들이 이벤트 루프를 통해 순차적으로 실행되는 것입니다. 이러한 방식으로 JavaScript는 비동기 작업을 효율적으로 처리할 수 있습니다.
이벤트 루프 구성요소
콜 스택
콜 스택은 코드 실행 중 발생하는 실행 컨텍스트들을 관리하는 스택 구조의 영역입니다. 함수가 호출되면, 해당 함수의 실행 컨텍스트가 콜 스택에 쌓이게 되고, 함수 실행이 완료되면 스택에서 해당 컨텍스트가 제거됩니다. 콜 스택은 LIFO (Last In, First Out) 방식으로 작동하기 때문에, 현재 진행 중인 실행 컨텍스트가 완료되면, 그 이전의 컨텍스트가 실행을 재개합니다.
힙
힙은 메모리 할당이 일어나는 구조화되지 않은 메모리 영역으로, 주로 객체와 같은 동적으로 크기가 변할 수 있는 데이터를 저장하는 데 사용됩니다. 콜 스택의 변수가 이 힙에 저장된 객체를 참조할 수 있습니다. 객체의 크기가 컴파일 타임이 아닌 런타임에 결정되기 때문에, 힙은 덜 조직화된 메모리 영역입니다.
태스크 큐
태스크 큐, 또는 이벤트 큐는 비동기 함수의 결과인 콜백 함수들이 일시적으로 보관되는 영역입니다. 예를 들어, setTimeout이나 setInterval의 콜백 함수, 그리고 이벤트 핸들러들이 이 큐에 보관됩니다. 태스크 큐는 이벤트 루프에 의해 관리되며, 콜 스택이 비었을 때 태스크 큐에서 콜백 함수들이 콜 스택으로 이동해 실행됩니다.
마이크로 태스크 큐
마이크로태스크 큐는 프로미스의 then, catch, finally와 같은 후속 처리 메서드의 콜백 함수들이 일시적으로 보관되는 영역입니다. 이 큐의 작업은 일반 태스크 큐의 작업보다 우선적으로 처리됩니다. 이벤트 루프는 마이크로태스크 큐에서 모든 작업이 완료될 때까지 계속해서 작업을 비우며 실행합니다.
이벤트 루프
이벤트 루프는 콜 스택이 비어 있는지와 태스크 큐 또는 마이크로태스크 큐에 대기 중인 작업이 있는지를 반복적으로 확인합니다. 콜 스택이 비었을 때, 이벤트 루프는 마이크로태스크 큐의 작업을 먼저 처리한 다음 태스크 큐에서 작업을 콜 스택으로 이동시켜 실행합니다. 이 과정은 지속적으로 반복되면서 비동기 작업들이 처리됩니다.
Web APIs
타이머 함수(setTimeout, setInterval), HTTP 요청, 그리고 이벤트 핸들러 등의 브라우저에서 제공하는 API를 가르키며 자바스크립트 엔진과는 달리 멀티스레드로 동작합니다.
비동기 프로그래밍 예시
콜백 함수 (Callback Functions)
콜백 함수는 다른 함수에 인수로 전달되는 함수입니다. 비동기 작업이 완료된 후에 실행되어야 할 코드를 콜백 함수로 작성하여 사용합니다.
function fetchData(callback) {
setTimeout(() => { // setTimeout을 사용해 비동기적으로 작업을 시뮬레이션
callback('데이터 받기 성공');
}, 1000);
}
fetchData((data) => {
console.log(data); // 데이터 받기 성공
});
프로미스 (Promises)
프로미스는 비동기 작업의 최종 성공 또는 실패를 나타내는 객체입니다. .then(), .catch(), .finally() 메서드를 사용하여 비동기 작업의 결과를 처리할 수 있습니다.
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('데이터 받기 성공');
}, 1000);
});
}
fetchData()
.then(data => console.log(data)) // 데이터 받기 성공
.catch(error => console.error(error));
async/await
async/await는 비동기 프로미스 코드를 동기적으로 보이게 하는 문법적 설탕입니다. 코드의 가독성을 높이고, 복잡한 비동기 로직을 간단히 할 수 있습니다.
async function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('데이터 받기 성공');
}, 1000);
});
}
async function asyncFunction() {
try {
const data = await fetchData();
console.log(data); // 데이터 받기 성공
} catch (error) {
console.error(error);
}
}
asyncFunction();
'Programming > JavaScript' 카테고리의 다른 글
디바운스와 스로틀 (0) | 2024.04.11 |
---|---|
Web Worker (0) | 2024.04.09 |
객체에서의 in 연산자 사용 시 주의사항 (0) | 2024.02.29 |
자바스크립트 컴파일러 에러와 런타임 에러 이해하기 (0) | 2023.11.11 |