
오늘은 자바스크립트의 Promise가 어떻게 동작하는지에 대해서 공부를 해보려고 합니다.
자바스크립트는 싱글 스레드 언어인데 ?
자바스크립트의 Promise는 비동기 처리를 할 때 사용합니다. 근데 신기한 부분이 있습니다. 자바스크립트는 싱글 스레드 언어인데 어떻게 비동기 처리를 하는 걸까요 ? 싱글 스레드의 특징 중 하나인 단일 작업 흐름: 한 번에 하나의 작업만 처리할 수 있습니다. 에 따르면 한 번에 하나의 작업만 처리하는데 "하나의 작업이 끝나기도 전에 다른 작업을 실행시킨다." 라는 개념이 이해가 되지 않습니다. 우선 동기 처리와 비동기 처리일 때를 분리해서 이때 자바스크립트가 어떻게 동작하는지 살펴보겠습니다.
이 사이트에서 자바스크립트의 동작을 시각화해줘서 이해하기 편한 거 같아요 ! 근데 오류가 좀 있는거 같습니다.
동기 처리
간단하게 예시 코드로 동작을 확인해 보겠습니다.
function Test1() {
console.log("test1");
}
function Test2() {
Test1();
console.log("test2");
}
function Test3() {
Test2();
console.log("test3");
}
Test3();
이제 실행시켜볼까요 ?
영상에서 잘 나왔다 싶이 함수의 실행 컨텍스트들이 순서대로 잘 쌓이고 실행되는 모습입니다.
비동기 처리
이제는 비동기 처리일 때도 한번 확인해 볼까요 ? 우선 Promise가 나오기 전에 비동기 처리를 위해 사용했던 setTimeout으로 확인해 보겠습니다.
setTimeout
코드입니다.
function Test1() {
console.log("test1");
}
function Test2() {
Test1();
console.log("test2");
}
function Test3() {
setTimeout(function setTimeoutCallback() {
console.log("test3");
}, 0);
Test2();
}
Test3();
Test3 함수를 실행시키면 setTimeout의 timer가 0이니 바로 실행되지 않을까요 ? 한번 결과를 확인해 보겠습니다 !
setTimeout 처리 과정
음 setTimeoutCallback 함수가 바로 실행되지 않고 나중에 실행된 Test1, Test2 그리고 Test3이 콜스택에서 사라졌을 때 실행되는 모습입니다.
왜 이런 결과가 나올까요 ?
정답은 바로 이벤트 루프 때문입니다. 위에서 언급했다싶이 자바스크립트는 싱글 스레드입니다. 하지만 이 자바스크립트가 실행되는 브라우저 환경에서는 비동기 작업을 처리하기 위한 Web API가 있습니다.
- 테스크 큐와 마이크로 테스크 큐는 콜백 큐의 종류입니다.
- Web API를 통해 비동기 작업을 처리
- 완료된 순서대로 비동기 작업은 콜백 큐로 이동됨
- 이벤트 루프
- 이벤트 루프는 콜스택과 콜백 큐를 반복하여 확인하면서 콜스택이 비워지면 콜백 큐의 작업을 콜스택으로 옮김
이런 원리로 브라우저에서는 setTimeout을 처리합니다.
이제는 오늘 글의 주제인 Promise에 대해서 살펴보겠습니다.
Promise
Promise는 setTimeout으로 비동기를 처리하는 것과 어떤 점이 다를까요 ? 그저 timer를 0으로 설정하는 것을 단순화 시키고 코드를 좀 더 가독성 있게 만들어주는 시멘틱 슈가일까요 ? 한번 확인해 보겠습니다.
function Test1() {
console.log("test1");
}
function Test2() {
Test1();
console.log("test2");
}
function Test3() {
Promise.resolve("promise").then(function promiseCallback(res) {
console.log(res);
});
Test2();
console.log("test3");
}
Test3();
이제 실행시켜볼게요.
오호 뭔가 setTimeout과 비슷하게 동작하는 거 같은대요 ? 다른 점이 있다면 위에서 setTimeout을 사용한 처리는 Task Queue에 있었는데 Promise는 Microtask Queue에 있네요.
setTimeout vs Promise
이제 이 둘의 차이에 대해서 좀 더 비교해 보겠습니다.
이런 식으로 코드를 짜면 어떻게 실행될까요 ?
setTimeout(function setTimeoutCallback() {
console.log("setTimeout");
});
Promise.resolve("promise").then(function promiseCallback(res) {
console.log(res);
});
둘 다 바로 처리되는 비동기 작업이고 setTimeout이 먼저 실행됐으니 setTimeout이 먼저 콘솔에 나올까요 ?

??? 이상하게 Promise부터 콘솔에 찍히네요.
영상을 살펴보면 위에서 언급한 Microtask Queue에 들어가 있는 promiseCallback이 먼저 콜스택으로 들어가고 그다음에 Task Queue에 있는 setTimeoutCallback이 콜스택으로 가는 모습을 볼 수 있는데요.
이러한 이유는 Microtask Queue의 우선순위가 Task Queue보다 높기 때문입니다. Microtask Queue가 비워지기 전까지는 Task Queue의 동작들이 콜스택으로 이동하지 않습니다.
setTimeout(function setTimeoutCallback() {
console.log("setTimeout");
});
setTimeout(function setTimeoutCallback() {
console.log("setTimeout");
});
setTimeout(function setTimeoutCallback() {
console.log("setTimeout");
});
setTimeout(function setTimeoutCallback() {
console.log("setTimeout");
});
setTimeout(function setTimeoutCallback() {
console.log("setTimeout");
});
Promise.resolve("promise").then(function promiseCallback(res) {
console.log(res);
});
이렇게 먼저 Task Queue에 여러 개를 쌓아도

Microtask Queue가 비워지기 전까지는 먼저 실행되지 않습니다.
Promise Method
이제 Promise에서 제공하는 정적 메서드 몇 개의 동작을 확인해 보겠습니다.
Promise.all
Promise.all은 모든 Promise가 완료된 후에 실행되는 메서드입니다.
const promise1 = new Promise(function promiseCallback1(resolve) {
setTimeout(function setTimeoutCallback1() {
resolve("promise1");
}, 3000);
});
const promise2 = new Promise(function promiseCallback2(resolve) {
setTimeout(function setTimeoutCallback2() {
resolve("promise2");
}, 2000);
});
const promise3 = new Promise(function promiseCallback3(resolve) {
setTimeout(function setTimeoutCallback3() {
resolve("promise3");
}, 1000);
});
Promise.all([promise1, promise2, promise3]).then(function promiseAllCallback(
values
) {
console.log(values);
});
결과를 확인해 보면 3초 뒤에

이런 로그가 찍히는 것이 확인됩니다.
흠 근데 영상에는 좀 오류가 있네요. Queue는 선입선출입니다. 그리고 Web API에서 완료된 작업부터 Queue에 담기는데 영상에서는 callback 함수가 실행될 때 바로 담기는 모습이네요.
Promise들이 다 끝난 뒤에 Promise all의 callback이 실행되는 모습입니다.
Promise.race
Promise.race은 받은 Promise 중에서 가장 먼저 완료된 것의 결과를 사용합니다.
const promise1 = new Promise(function promiseCallback1(resolve) {
setTimeout(function setTimeoutCallback1() {
console.log("setTimeoutCallback1");
resolve("promise1");
}, 3000);
});
const promise2 = new Promise(function promiseCallback2(resolve) {
setTimeout(function setTimeoutCallback2() {
console.log("setTimeoutCallback2");
resolve("promise2");
}, 2000);
});
const promise3 = new Promise(function promiseCallback3(resolve) {
setTimeout(function setTimeoutCallback3() {
console.log("setTimeoutCallback3");
resolve("promise3");
}, 1000);
});
Promise.race([promise1, promise2, promise3]).then(function promiseAllCallback(
value
) {
console.log(value);
});
이런 식으로 코드를 짜면

이런 결과가 나오는군요.
영상을 확인해 보면 (Task Queue에 완료되지 않은 작업이 쌓이는건 오류입니다.) 먼저 완료된 setTimeoutCallback3먼저 콜스택으로 오고 바로 promiseAllCallback이 Microtask Queue로 생성돼 콜스택으로 이동 후 처리가 되고 뒤에 늦게 처리되는 setTimeoutCallback1, setTimeoutCallback2가 실행되는 모습입니다.
미루고 미루던 Promise의 동작에 대해서 공부를 해보고 정리를 해봤습니다.
잘못된 정보가 있거나 오류가 있으면 피드백 주시면 감사하겠습니다.
'frontend' 카테고리의 다른 글
| (React 19 + Next js 15 + Panda Css) 1. 프로젝트 세팅 (1) | 2024.06.11 |
|---|---|
| 웹 어셈블리 찍먹 ! (0) | 2024.05.23 |
| No.1 Css 라이브러리 "Panda Css" (0) | 2024.05.06 |
| 🚀 "우당탕탕 도서관" 프론트엔드 개발자 글쓰기 커뮤니티 2기 모집 안내 🚀 (0) | 2024.05.04 |
| ??? : 추상화 잘합니다. (대충 할 말 빙빙 돌려 말한다는 뜻) (0) | 2024.04.01 |