FrontEnd Develop
비동기처리의 이해 : Promise, async/await 그리고 MicroTask?
Frisbeen
2025. 5. 19. 19:06
자바스크립트는 단일 스레드 기반 언어지만, 웹 API와 이벤트 루프를 통해 비동기 처리를 지원합니다.
이 글에서는 Promise.then(), async/await, setTimeout() 등이 함께 혼재될 때의 실행 순서와 내부 구조를 이해하기 쉽게 정리합니다.
1. JavaScript의 실행 환경 : 이벤트 루프 구조
자바스크립트는 다음과 같은 실행 큐를 기반으로 동작합니다:
- Call Stack: 현재 실행 중인 코드
- Microtask Queue: 우선순위 높은 비동기 작업 → Promise.then(), await, queueMicrotask() 등
- Macrotask Queue: 일반적인 비동기 → setTimeout(), setInterval() 등
실행 순서:
- 동기 코드 (call stack) 먼저 실행
- Microtask Queue의 작업 모두 소진
- Macrotask Queue의 작업 하나 실행
- 다시 1번부터 반복
2. Promise와 async/await의 개념 정리
Promise
- 비동기 작업의 상태(state)를 표현하는 객체
- .then(), .catch(), .finally()를 통해 후속 작업을 예약
- Microtask Queue에 후속 작업을 등록
Promise.resolve().then(() => console.log('then 실행'));
async/await
- async 함수는 항상 Promise를 반환
- await은 해당 Promise가 resolve될 때까지 함수를 일시 중단(pause)
- 중단 이후 코드는 Microtask로 실행됨
async function fetchData() {
console.log('시작');
const res = await fetch('/api');
console.log('끝');
}
3. 실제 혼재 예제 분석
비동기 우선순위가 높은 Promise , Async(어차피 Promise) 이니 둘 다 MicroTask Queue에 들어갑니다.
그에 따라, setTimeout 보다 먼저 3. Promise then, 그리고 5. await 이후에 2.setTimeout이 실행되는 겁니다.
동기와 비동기가 혼재되어있을때, 우선순위를 기반으로 비동기들의 동기성이 생긴다는 것입니다.
console.log('1. 동기');
setTimeout(() => {
console.log('2. setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('3. Promise then');
});
(async function () {
console.log('4. async 함수 시작');
await null;
console.log('5. await 이후');
})();
console.log('6. 동기 끝');
예상 실행 순서
1. 동기
4. async 함수 시작
6. 동기 끝
3. Promise then
5. await 이후
2. setTimeout
설명:
- console.log('1'), 4, 6 → Call Stack에서 즉시 실행
- Promise.then()과 await 이후 → Microtask Queue
- setTimeout → Macrotask Queue → 마지막에 실행됨
4. 동기 실행처럼 보이지만, 실제로는 비동기인 이유?
- then().then() 체이닝이나 await await 연속 구문은 동기처럼 보여도, 실제로는 모두 마이크로태스크 큐에 예약된 작업입니다.
- 따라서 겉보기에는 순차적이지만, 본질은 비동기 처리입니다.
5. 마이크로태스크는 무엇인가?
- 비동기 중에서도 가장 먼저 실행됨
- 예: Promise.then, await, queueMicrotask
- 콜스택이 비는 즉시 처리됨 → setTimeout보다 빠름
6. 결론
동기 코드 | Call Stack | 즉시 실행 | console.log() |
마이크로태스크 | Microtask Queue | Call Stack이 비자마자 | Promise.then, await 이후 |
매크로태스크 | Macrotask Queue | Microtask가 끝난 후 | setTimeout, setInterval |
비동기 코드가 복잡하게 보일 수 있지만, 이벤트 루프의 실행 순서를 정확히 이해하면 대부분의 동작은 예측 가능합니다.
실제 프로젝트에서 async/await와 Promise, setTimeout이 섞일 때는 이 흐름을 바탕으로 디버깅하고, 예측 가능한 코드를 작성할 수 있습니다.