🃏 치트시트 · 2026년 6월 15일
비동기와 타이밍, 콜백부터 async/await까지의 발전사
싱글 스레드 JavaScript 가 어떻게 동시에 여러 일을 처리하는지, 이벤트 루프부터 Promise·async/await·AbortController·AsyncIterator 까지 메커니즘으로 풀어본다. setTimeout 0 이 왜 즉시가 아닌지, 마이크로태스크가 왜 먼저인지, 콜백이 왜 지옥이 되는지를 차례로 답한다.
시작 의문
- 다음 코드의 출력 순서를 맞춰보자.
- JavaScript 는 싱글 스레드라는데, 어떻게 setTimeout 과 Promise 가 "동시에" 진행되는가?
- setTimeout(fn, 0) 은 왜 즉시가 아닌가? 0 밀리초인데도 왜 그렇게 느린가?
- 같은 비동기인데 왜 Promise 가 setTimeout 보다 항상 먼저 실행되는가?
한 단계 뒤로, JavaScript 는 왜 비동기가 필요한가
- 싱글 스레드의 본질
- 싱글 스레드 (single-threaded)
- JavaScript 엔진은 싱글 스레드지만, 그것을 실행하는 호스트 환경 (브라우저, Node.js) 은 멀티 스레드
- Call Stack
타이밍의 시작, setTimeout 과 setInterval
- 비동기를 가장 먼저 만나는 곳은 타이머 API 다.
- 타이머 API
- "최소" 다.
- 최소
콜백, 첫 번째 시도
- 타이머처럼 비동기 결과를 받는 가장 원시적인 방법은 콜백 함수 (callback) 다.
- 콜백 함수 (callback)
- "에러 우선 콜백 (error-first callback)"
- 에러 처리
resolve 와 reject, 봐도 봐도 헷갈리는 그것
- Promise 본격 도입 전에, 가장 헷갈리는 부분을 먼저 정리하자.
- resolve, reject 가 누구 손에 있는가
- 요청하는 쪽
- 처리하는 쪽
Promise, 상태 기반 처리
- 이제 Promise 자체를 들여다보자.
- pending
- fulfilled
- rejected
async/await, Promise 의 문법 설탕
- 한 키워드로 동기처럼
- 마치 동기 코드처럼
- async 함수는 항상 Promise 를 반환.
- 에러는 try/catch 로.
동시성 제어, Promise 의 정적 메서드 네 형제
- 여러 Promise 를 한꺼번에 다룰 때 쓰는 네 메서드를 차례로 보자.
- 하나라도
- Promise.all 은 일찍 끝난 성공을 버린다.
- 상태와 값
마이크로태스크 vs 매크로태스크, 우선순위의 비밀
- 이제 글머리의 첫 질문으로 돌아갈 차례.
- 왜 Promise.then 이 setTimeout 0 보다 항상 먼저
- 핵심
- 마이크로태스크는 "이번 동기 작업이 끝나자마자 즉시", 매크로태스크는 "다음 이벤트 루프 틱에 한 개씩".
fetch 와 AbortController, 비동기 작업의 취소
- Promise 와 async/await 가 자리 잡으면서 자연스럽게 마주치는 다음 문제는 취소 다.
- 취소
- 구조
- 모든 비동기 API 가 받을 수 있는 표준
Generator 와 AsyncIterator, 흐름을 일시정지하는 또 다른 길
- Promise 와 async/await 외에도 JS 에는 비동기 흐름을 다루는 다른 도구가 있다.
- Generator
- AsyncIterator
- 이터레이터
requestAnimationFrame, 화면 갱신과 발 맞추기
- 타이머와 Promise 외에 브라우저가 제공하는 또 하나의 비동기 스케줄링이 (rAF) 이다.
- 렌더링 사이클과 동기화
- 마이크로태스크도 매크로태스크도 아니다
- 같은 프레임에 반영
정리, 비동기 스케줄링의 위계
- 다섯 개의 큐 / 단계를 시간 순으로 정리하면.
- 암기보다 메커니즘 이해가 더 안전
- 콜백은 사라지지 않았다.
- Q1
참고 자료
- ECMA-262, Jobs and Host Operations
- ECMA-262, Promise Objects
- WHATWG HTML, Event loop