🧭 동기(Sync) vs 비동기(Async)
JavaScript는 브라우저(Web API)와 Node.js(libuv)의 도움을 받아 비동기(Async) 작업을 효율적으로 처리합니다.
1️⃣ 동기 (Synchronous)
📘 개념
모든 작업이 순차적으로 하나씩실행 되는 방식입니다. 하나의 작업이 끝나야 다음 작업이 실행됩니다.
📌 특징
- 실행 순서 명확 → 예측 쉬움
- 단점: 시간이 오래 걸리는 작업이 있으면 전체 흐름이 멈춤(Blocking)
🧐 예시
console.log("1");
for (let i = 0; i < 1e9; i++) {}
console.log("2"); // for문이 끝날 때까지 기다림
2️⃣ 비동기 (Asynchronous)
📘 개념
오래 걸리는 작업을 백드라운드로 위임하고, JS는 멈추지 않고 다음 코드를 실행합니다.
📌 특징
- Non-blocking
- 네트워크, 파일 작업, 타이머 등에 유리
- 더 나은 UI/성능
🧐 예시
console.log("A");
setTimeout(() => console.log("B"), 0);
console.log("C");
// A → C → B
3️⃣ JavaScript 런타임 내부 구조
JS 비동기의 핵심은 브라우저/Node.js의 Event Loop 메커니즘입니다.
🔹 Call Stack
JavaScript 엔진이 코드를 실행하는 스택. 한 번에 하나씩만 실행 가능
🔹 Web API / Node API
- 브라우저: setTimeout, DOM 이벤트, fetch 등
- Node: libuv가 파일/네트원 I/O 처리
💡 libuv는 Node.js의 비동기 I/O(Non-blocking I/O) 엔진입니다.
🔹 Callback Queue (Macrotask Queue)
- setTimeout, setInterval, I/O 콜백 등이 들어오는 큐
🔹 Microtask Queue
- Promise.then, catch, finally, queuMicrotask 등이 들어옴 → 우선순위: Microtask > Macrotask
🔹 Event Loop
- Call Stack이 비면 큐의 작업을 스택으로 가져와 실행
- 한 번의 루프에서 모든 마이크로태스크를 먼저 처리
4️⃣ Microtask vs Macrotask 심화
console.log("start");
setTimeout(() => console.log("timeout"), 0);
Promise.resolve()
.then(() => console.log("promise1"))
.then(() => console.log("promise2"));
console.log("end");
📌 실행 순서
start
end
promise1
promise2
timeout
- Promise.then → Microtask
- setTimeout → Macrotask
- Microtask가 항상 먼저 실행됨
5️⃣ 비동기 표현 방식
🔹 Callback
fs.readFile('data.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
⚠️ 콜백 지옥 발생 → 유지보수 어려움
🔹 Promise
fetch('/api')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
주요 유틸
- Promise.all
- Promise.allSettled
- Promise.race
- Promise.any
🔹 async / await (현재 표준)
async function load() {
try {
const res = await fetch('/user');
const data = await res.json();
console.log(data);
} catch (err) {
console.error(err);
}
}
병령 실행
// ❌ 직렬(느림)
const a = await fetch('/a');
const b = await fetch('/b');
// ✅ 병렬(빠름)
const [a, b] = await Promise.all([fetch('/a'), fetch('/b')]);