자바스크립트에서 비동기를 제어하는 4가지 방법
자바스크립트에서 비동기를 제어하는 방법에는 총 "4가지"가 있다고 알려져 있다.
1) 콜백함수
2) 프로미스
3) async/await
4) 제너레이터
각각의 사용 방법 차이를 예제 코드와 함께 살펴보면 아래와 같다:
1) 콜백함수
function asyncFunction(callback) {
setTimeout(() => {
callback('Hello, world!');
}, 1000);
}
asyncFunction((result) => {
console.log(result);
});
2) 프로미스
function asyncFunction() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Hello, world!");
}, 1000);
});
}
asyncFunction()
.then((result) => console.log(result))
.catch((err) => console.log(err));
3) async/await
function asyncFunction() {
return new Promise((resolve, resject) => {
setTimeout(() => {
resolve("Hello, world!");
}, 1000);
});
}
async function main() {
try {
const result = await asyncFunction();
console.log(result);
} catch (err) {
console.log(err);
}
}
main();
4) 제너레이터
function* asyncFunction() {
yield new Promise((resolve) => {
setTimeout(() => {
resolve('Hello, world!');
}, 1000);
});
}
const gen = asyncFunction();
const promise = gen.next().value;
promise
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
});
문제 💡
아래와 같은 코드에서 콘솔에 찍히는 결과는 어떻게 나올까?
문제
console.log("start");
// 일반
console.log("1");
// Callback
setTimeout(() => {
console.log("2");
}, 0);
// Promise
const promise = new Promise((resolve, reject) => {
resolve("3");
});
promise.then((result) => console.log(result));
// async/await
const promise2 = new Promise((resolve) => {
resolve("4");
});
async function test() {
try {
const res = await promise2;
console.log(res);
} catch (e) {
console.log(e);
}
}
test();
// Generator
function* generator() {
yield "5";
}
const gen = generator();
console.log(gen.next().value);
console.log("end");
정답
start → 1(일반) → 5(제너레이터)→ end(일반) → 3(promise) → 4(async/await) → 2(callback)
해설 ✍️
이벤트 루프에 대해 알아보자
왜 정답이 이렇게 나왔는지 알려면 이벤트 루프(event loop)의 개념에 대해 알아야 한다.
이벤트 루프란 자바스크립트의 동시성을 관리하는 핵심 메커니즘이다.
그 메커니즘을 간단하게 한 번, 자세히 한 번 이렇게 두 번으로 나누어 보며 이해해보자.
1) 자바스크립트 비동기 메소드의 브라우저 상 기본 동작 원리
자바스크립트에서 비동기 메소드는 기본적으로 아래와 같이 작동한다.
실행은 콜스택(call stack)에서 이루어진다.
➡️ 시간이 오래 걸리는(비동기) 메소드의 경우 작업의 효율성을 위해 큐(queue)로 이동해 대기한다.
➡️ 다른 작업이 다 끝나면 다시 콜스택으로 호출 해 실행한다.
2) 큐(queue)의 종류: 매크로 테스크 큐(Macrotask Queue)와 마이크로 테스크 큐(Microtask Queue)
좀 더 자세히 살펴보면, 사실 비동기 메소드들이 대기하는 큐(queue)는 작업 성질에 따라 여러 종류가 있다.
대표적인 2가지는 매크로테스크 큐(Macrotask Queue, 일반적인 큐는 보통 이 매크로테스크 큐를 가리킨다)와 마이크로테스크 큐(Microtask Queue)이다.
이러한 큐는 종류에 따라 실행 순서가 다르다.
예를 들어, 마이크로테스크 큐에 들어간 api는 매크로테스크 큐의 api보다 먼저 처리된다.
즉, 1️⃣ call stack ➡️ 2️⃣ Microtask Queue ➡️ 3️⃣ Macrotask Queue 순서로 처리된다.
api 종류에 따라 다른 큐에 배치되며 위에서 언급된 비동기 메소드들의 경우,
- Promise, async/await ➡️ 마이크로 테스크 큐
- setTimeout ➡️ 매크로 테스크 큐
- Generator 의 경우, 비동기 기능을 가지지만 큐로 이동되지 않고 첫 콜 스택에서 바로 처리된다.
결론
위 내용을 요약하면 아래와 같다.
이벤트 루프란
큐에는 여러 종류가 있으며, 각 큐는 실행 순서가 있다.
마이크로 큐는 매크로 큐의 작업보다 우선적으로 실행된다.
자바스크립트에서 쓰이는 비동기 처리의 4가지 방법은 각각의 큐/스택을 거쳐 실행된다.
1) 콜백함수: call stack ➡️ Macrotask Queue ➡️ call stack
2) 프로미스: call stack ➡️ Microtask Queue ➡️ call stack
3) async/await: 프로미스 기반이므로 프로미스와 작동원리 같음.(즉, 마이크로 큐)
4) 제너레이터: call stack 에서 바로 처리. (비동기지만 Queue로 이동하지 않고 call stack에서 바로 처리됩니다.)
따라서 위 문제의 코드 동작 결과는 아래와 같다:
1️⃣ call stack (start 1 5 end) ➡️ 2️⃣ Microtask Queue (3 4) ➡️ 3️⃣ Macrotask Queue (2)
Reference
1.
What the heck is the event loop | Philip Roberts | JSConf EU
2.
Microtasks and (Macro)tasks in Event Loop
https://medium.com/@saravanaeswari22/microtasks-and-macro-tasks-in-event-loop-7b408b2949e0
Microtasks and (Macro)tasks in Event Loop
JavaScript has a concurrency model based on an event loop, which is responsible for executing the code, collecting and processing events…
medium.com
Last Updated on 2024/07/02
'Programming > 웹 프론트엔드' 카테고리의 다른 글
[React] 리액트에서의 상태 관리, 무엇이고 어떻게 해야할까 (0) | 2024.07.10 |
---|---|
[React] 비동기함수 Hook으로 다루기 (0) | 2023.05.30 |
[React] 컴포넌트 분리, 어떻게 해야할까? (0) | 2023.03.19 |