[JavaScript] 비동기 처리 - 이벤트 루프, setTimeout, Promise, async/await

ttining·2025년 4월 22일

JavaScript는 싱글 스레드 언어임에도 비동기 작업을 효과적으로 처리할 수 있는 구조를 가지고 있으며, 그 핵심에는 이벤트 루프(Event Loop)가 있다.
이벤트 루프의 작동 원리와 주요 비동기 처리 방식인 setTimeout, Promise, async/await까지 개념과 주의사항에 대해 알아보자.


📍 JavaScript는 왜 비동기가 필요한가?

JavaScript는 싱글 스레드 기반으로 동작한다.
즉, 한 번에 하나의 작업만 처리할 수 있기 때문에 I/O 작업(네트워크, 파일, 타이머 등)을 동기적으로 처리하면 전체 프로그램이 멈추게 된다.
이를 해결하기 위해 비동기 처리가 도입되었고, 이벤트 루프는 이를 가능하게 하는 핵심 메커니즘이다.



📍 이벤트 루프(Event Loop)의 개념과 동작 원리

1️⃣ 구조 개요

JavaScript 실행 환경(예: 브라우저, Node.js)은 다음과 같은 구성 요소로 이루어져 있다.

  • Call Stack : 현재 실행 중인 함수들을 저장하는 스택
  • Web APIs (브라우저 환경) : setTimeout, fetch, DOM 이벤트 등 브라우저가 제공하는 비동기 API
  • Callback Queue (Task Queue) : 비동기 작업이 완료되어 콜백이 대기하는 큐
  • Microtask Queue : Promise.then, queueMicrotask 등의 마이크로태스크 대기열
  • Event Loop : 위 요소들을 조율하며 태스크들을 Call Stack에 넣는 루프

2️⃣ 흐름 요약

  1. Call Stack이 비면,
  2. Microtask Queue를 먼저 확인하고 처리하고,
  3. 그 후에 Callback Queue(Task Queue)에서 태스크를 가져와 실행한다.


📍 비동기 처리 방식의 종류

1️⃣ setTimeout

브라우저의 Web API를 통해 타이머를 설정한 뒤, 지정 시간 이후 콜백을 큐에 넣는다.

console.log('시작');

setTimeout(() => {
  console.log('1초 뒤 실행');
}, 1000);

console.log('끝');

👀 출력 순서

시작
끝
1초 뒤 실행

설정한 시간이 지난 직후 실행된다는 보장은 없음 → 이벤트 루프가 빈 상태여야 실행된다.


2️⃣ Promise

비동기 작업의 결과를 표현하는 객체.
생성 시 바로 실행되며, 완료되면 Microtask Queue에 콜백을 넣는다.

console.log('시작');

Promise.resolve().then(() => {
  console.log('Promise');
});

console.log('끝');

👀 출력 순서

시작
끝
Promise

then/catch/finally는 마이크로태스크로 처리되며 setTimeout보다 우선이다.


3️⃣ async/await

Promise를 더 읽기 쉬운 방식으로 작성하게 해주는 문법적 설탕(Syntax Sugar).

async function fetchData() {
  console.log('fetch 시작');
  const res = await Promise.resolve('응답');
  console.log(res);
}

fetchData();

console.log('동기 코드 실행');

👀 출력 순서

fetch 시작
동기 코드 실행
응답

await은 해당 Promiseresolve될 때까지 기다리고, 이후의 코드를 마이크로태스크로 밀어넣는다.



⚠️ 주의할 점

1️⃣ setTimeout 최소 지연 시간

브라우저에서는 setTimeout(fn, 0)이라도 최소 4ms의 지연이 발생할 수 있다. (특히 오래된 브라우저)


2️⃣ 무한 루프의 위험성

동기 함수 내에서 시간이 오래 걸리는 코드를 작성하면 이벤트 루프가 막혀 전체 UI가 멈춘다.

while (true) {} // 🚫 절대 ㄴㄴ 🚫

3️⃣ Promise는 자동으로 예외를 캡처하지 않음

async function broken() {
  throw new Error('실패');
}

broken(); // 처리되지 않은 Promise 예외 발생

// 반드시 처리
broken().catch(console.error);


✅ 정리

상황추천 방식비고
간단한 지연setTimeout타이머 기반 지연
네트워크 요청 등 외부 작업 처리Promise.then(), .catch() 활용
Promise 기반 코드 간결하게 작성async/awaittry/catch와 함께 사용 권장
여러 비동기 작업 병렬 실행Promise.all()병렬 처리 및 실패 관리 필요
이벤트 루프 흐름 실험 및 디버깅console.log, setTimeout, Promise 조합실행 순서 분석에 도움
profile
내가 보려고 만든 벨로그 *'-'*

0개의 댓글