JavaScript

[JavaScript] task queue와 microtask queue 동작 순서

쿠키는 서비스 2023. 11. 13. 16:00
반응형
console.log('Start');

setTimeout(function() {
  console.log('Timeout callback');
}, 0);

console.log('End');​
console.log(1);

setTimeout(() => console.log(2));

Promise.resolve().then(() => console.log(3));

Promise.resolve().then(() => setTimeout(() => console.log(4)));

Promise.resolve().then(() => console.log(5));

setTimeout(() => console.log(6));

console.log(7);

 

이 코드의 출력을 맞춰보세요!

(정답은 맨 아래)

 

지난 주 면접 질문으로 이벤트 루프와 관련된 개념을 설명하라는 질문을 받았다. 

이 정도 내용을 알고 있으면 된다.

이벤트 루프란(Event Loop)?

이벤트 루프는 JavaScript 런타임 환경에서 비동기 코드의 실행을 조절하는 메커니즘이다. 

자바스크립트는 싱글 스레드 언어이기 때문에 한 번에 하나의 작업만을 처리할 수 있다. 웹 브라우저나 Node.js환경에서는 여러 가지 이벤트(마우스클릭, HTTP 요청 등)가 발생하고 이러한 이벤트들은 비동기적으로 처리된다. 이를 위한 작업을 하는 것이 이벤트 루프이다.

 

- 콜스택(Call Stack)

현재 실행 중인 코드의 컨텍스트를 저장한다.

코드가 실행되면 해당 함수의 컨텍스트가 스택에 쌓이고 함수가 종료되면 스택에서 제거된다.

 

- 메세지 큐(Message Queue)

비동기 작업이 완료되면 해당 작업의 콜백 함수나 이벤트가 여기에 추가된다.

메세지 큐는 FIFO(선입선출) 구조를 가지며 작업이 순서대로 대기한다.

 

- 이벤트 루프(Event Loop)

콜 스택과 메세지 큐를 주시하고 있고 호출 스택이 비어있을 때 메세지 큐의 작업을 콜 스캑으로 이동시킨다.

이벤트 루프를 통해 비동기 작업이 순차적으로 실행된다.

 

이벤트 루프의 동작 과정

1. 콜스택이 비어있으면 이벤트 루프는 메세지 큐에서 작업을 가져와 콜스택에 추가한다.

2. 콜스택에 있는 코드가 실행되면서 해당 함수의 컨텍스트가 스택에서 제거된다.

3. 메세지 큐에서 대기 중인 작업이 있다면 콜스택이 비어있을 때 해당 작업이 콜스택에 추가되고 실행된다.

4. 이 과정이 계속 반복되면서 비동기 작업이 순차적으로 실행된다.

 

이러한 과정을 통해 JavaScript는 비동기 코드를 처리하고, 동시에 여러 이벤트에 대응할 수 있다.

 

정답) 1 7 3 5 6 4

console.log(1);
// The first line executes immediately, it outputs `1`.
// Macrotask and microtask queues are empty, as of now.

setTimeout(() => console.log(2));
// `setTimeout` appends the callback to the macrotask queue.
// - macrotask queue content:
//   `console.log(2)`

Promise.resolve().then(() => console.log(3));
// The callback is appended to the microtask queue.
// - microtask queue content:
//   `console.log(3)`

Promise.resolve().then(() => setTimeout(() => console.log(4)));
// The callback with `setTimeout(...4)` is appended to microtasks
// - microtask queue content:
//   `console.log(3); setTimeout(...4)`

Promise.resolve().then(() => console.log(5));
// The callback is appended to the microtask queue
// - microtask queue content:
//   `console.log(3); setTimeout(...4); console.log(5)`

setTimeout(() => console.log(6));
// `setTimeout` appends the callback to macrotasks
// - macrotask queue content:
//   `console.log(2); console.log(6)`

console.log(7);
// Outputs 7 immediately.

1. 1, 7은 큐를 거치지 않기 때문에 바로 출력된다.

2. 마이크로테스트 큐

- console.log(3), setTimeout(...4), console.log(5)

- 3과 5가 바로 출력된다.

- macrotask queue에는 console.log(2), console.log(6)가 들어있다.

3. 마이크로테스크 큐가 비어있으면 macrotask queue가 실행되면서 2, 6,4가 출력된다.

 

macro task queue와 task queue가 헷깔리는데 일반적으로 같은 것으로 간주되어 이야기하기도 한다.

가장 중요하게 기억되는 것은 microtask queue가 가장 먼저 실행되는 큐라는 점!!

microtask queue는 promise의 then에 들어가는 작업을 기억하면 된다.

 

Task queue

console.log('Start');

Promise.resolve().then(function() {
  console.log('Promise microtask');
});

console.log('End');

 

Microtask queue

Start
End
Promise microtask   // Microtask Queue의 작업이 먼저 실행됨
Timeout callback   // Task Queue의 작업이 실행됨

 

이벤트루프가 Task queue의 작업은 한 번에 한 개, Microtask queue의 작업은 한 번에 모두를 call stack에 넣는다는 것을 유의하자. 참조의 두 번째 링크에서 예제를 직접 돌려볼 수 있으니 꼭!!! 해보기!!

 

참조)

https://javascript.info/event-loop

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

반응형