이벤트 루프 기본 원리
- 매크로태스크 큐에서 가장 오래된 태스크(예: <script> 실행, 사용자 이벤트, setTimeout 콜백 등)를 꺼내 실행합니다.
- 실행 중인 매크로태스크가 끝나면 마이크로태스크 큐에 쌓인 모든 태스크(주로 프라미스의 .then 핸들러, queueMicrotask 등)를 처리합니다.
- 모든 마이크로태스크가 완료되면, 필요한 경우 렌더링이 진행됩니다.
- 매크로태스크 큐에 새로운 태스크가 있으면 1번으로 돌아갑니다.
매크로태스크와 마이크로태스크의 차이
매크로태스크
- 사용자 이벤트, 네트워크 요청, setTimeout 등과 같이 브라우저 외부에서 스케줄되는 작업들이 해당합니다.
- 태스크는 들어온 순서대로 처리되며, 각 매크로태스크 실행이 끝나면 마이크로태스크 큐를 먼저 비웁니다.
마이크로태스크
- 프라미스 핸들러(.then, .catch, .finally), queueMicrotask()로 스케줄된 작업 등이 포함됩니다.
- 현재 매크로태스크가 완료되면, UI 업데이트나 새로운 매크로태스크 실행 전에 마이크로태스크 큐가 모두 처리됩니다.
우선순위
- 마이크로태스크가 매크로태스크보다 우선적으로 처리됩니다.
긴 작업 분할:매크로태스크 활용
무거운 작업 하나가 실행되는 동안 사용자 이벤트가 처리되지 않아 브라우저가 '멈춘 것처럼' 보일 수 있습니다. 이 경우 작업을 여러 작은 조각(태스크)으로 분할하면 이벤트 루프가 중간중간 다른 태스크(예: 사용자 클릭, 화면 렌더링)를 처리할 수 있습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>긴 작업 분할</title>
<style>
#progress { width: 400px; height: 20px; background: #ddd; position: relative; }
#fill { height: 100%; background: #42a5f5; width: 0; }
#status { margin-top: 5px; }
</style>
</head>
<body>
<h1>배열 처리 진행률</h1>
<div id="progress"><div id="fill"></div></div>
<div id="status">0%</div>
<button id="start">시작</button>
<script>
const startBtn = document.getElementById('start');
const fill = document.getElementById('fill');
const status = document.getElementById('status');
const total = 5000000;
const chunkSize = 50000;
let processed = 0;
function processArray() {
const end = Math.min(processed + chunkSize, total);
for (let i = processed; i < end; i++) {
// 간단한 계산 작업
Math.pow(i, 2);
}
processed = end;
const percent = (processed / total) * 100;
fill.style.width = `${percent}%`;
status.textContent = `${Math.floor(percent)}%`;
if (processed < total) {
setTimeout(processArray, 0);
} else {
alert('처리 완료!');
}
}
startBtn.addEventListener('click', () => {
processed = 0;
processArray();
});
</script>
</body>
</html>
- setTimeout(...,0): 작업을 청크로 분할하여 UI 반응성 유지
마이크로태스크를 사용한 비동기 실행
queueMicrotask()를 사용하면 마이크로태스크 큐에 함수를 추가할 수 있습니다. 마이크로태스크는 현재 매크로태스크가 완료된 직후, 렌더링 전에 실행되므로 상태 일관성이 보장됩니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>마이크로태스크 활용</title>
<style>
#display { padding: 10px; border: 1px solid #ccc; }
</style>
</head>
<body>
<h1>상태 업데이트</h1>
<button id="updateBtn">상태 변경</button>
<div id="display">현재 상태: 초기값</div>
<script>
const updateBtn = document.getElementById('updateBtn');
const display = document.getElementById('display');
let state = '초기값';
updateBtn.addEventListener('click', () => {
state = '변경됨';
console.log('클릭 후 상태:', state);
queueMicrotask(() => {
display.textContent = `현재 상태: ${state}`;
console.log('마이크로태스크 실행:', state);
});
});
</script>
</body>
</html>
웹 워커: 병렬 실행으로 부하 분산
만약 아주 무거운 연산이 필요하다면, 웹 워커를 사용하여 별도의 스레드에서 작업을 처리할 수 있습니다. 웹 워커는 메인 스레드와 독립적인 이벤트 루프를 가지며, 메시지를 통해 메인 스레드와 데이터를 주고받을 수 있습니다. 단, 웹 워커는 DOM에 직접 접근할 수 없으므로, UI 업데이트가 필요한 경우 메인 스레드와의 협력이 필요합니다.
<!-- worker.js -->
self.onmessage = (e) => {
const n = e.data;
function fib(n) {
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}
self.postMessage(fib(n));
};
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>웹 워커 피보나치</title>
</head>
<body>
<h1>피보나치 계산</h1>
<input type="number" id="numberInput" value="40" min="1">
<button id="calcBtn">계산</button>
<div id="result">결과: </div>
<script>
const calcBtn = document.getElementById('calcBtn');
const numberInput = document.getElementById('numberInput');
const result = document.getElementById('result');
calcBtn.addEventListener('click', () => {
const worker = new Worker('worker.js');
worker.postMessage(parseInt(numberInput.value));
worker.onmessage = (e) => {
result.textContent = `결과: ${e.data}`;
worker.terminate();
};
worker.onerror = (e) => console.error('워커 오류:', e);
});
</script>
</body>
</html>
'브라우저' 카테고리의 다른 글
Selection과 Range API로 텍스트 선택 및 조작하기 (0) | 2025.02.21 |
---|---|
MutationObserver로 DOM 변화 실시간 제어하기 (0) | 2025.02.21 |
외부 리소스 로딩 감지:onload와 onerror 이벤트 (0) | 2025.02.21 |
defer와 async로 최적화된 스크립트 로딩 (0) | 2025.02.21 |
HTML 문서 생명주기 (0) | 2025.02.21 |