0. 이론

NodeJS Event Loop 동작 원리 (feat. process.nextTick)

Beginner:) 2024. 3. 30.
320x100

먼저, 이 이론을 왜 알아야 하는가에 대한 소스코드를 먼저 설명한다.

 

아래의 소스코드에서 "test start !", "1s END!", "test end!"의 출력 순서와 시간은 어떻게 될까?

("test start!"의 시간은 0초, for문은 약 3초가 걸린다고 가정한다)

console.log("test start! ", new Date().toString());

setTimeout(function() { console.log("1s END! ", new Date().toString()); }, 1000);

for(var i = 0; i < 5000000000; i++) {

}

console.log("test end! ", new Date().toString());

 

 

"test start !"(0초) -> "1s END!"(1초) -> "test end!"(3초)라고 생각하면 틀린 것이다. 

 

정답은 "test start!"(0초) -> "test end!"(3초) -> "1s END!"(3초)이다.

 

setTimeout이 1초 딜레이인데, 왜 3초뒤에 출력이 되었을까?

 

이를 알려면 Call Stack과 Event Loop에 대해서 이해하여야 한다.

 

먼저 전반적인 Call Stack과 Event Loop(Event Queue)에 대해서 잘 모른다면 아래의 유튜브를 보고 온다.

https://www.youtube.com/watch?v=v67LloZ1ieI

 


Event Loop

먼저 Event Loop에 순서는 아래와 같다.

 

 

 

Call Stack

현재 실행중인 함수의 정보를 저장한다. 이 함수가 종료되면 스택에서 제거된다.

Event Loop는 이 콜 스택의 상태를 모니터링하고, 비어있다면 Event Loop를 수행한다.

 

Timer

setTimeout() 또는 setInterval()과 같은 타이머들의 콜백을 실행한다.

Timer 함수들을 바로 Queue에 쌓는 것이 아닌, min-heap이라는 최솟값에 유리한 알고리즘을 사용하는데, min-heap에서 대기하다 타이머의 조건에 충족되면 Timer Queue에 쌓이게 되고 Callback이 실행된다.

 

Pending I/O

File, Network등에 대한 이벤트를 저장한다. 다만 이 단계에서 Callback을 실행하는 것은 아니고 Pending Queue에 저장했다가 이후 Poll단계에서 실행된다. 

 

Idle, prepare

개발자(또는 사용자)가 직접 접근하진 않는 단계로 생략하도록 한다.

다만 Pending에서 Poll로 넘어가는 데의 준비과정이라 생각하면 된다. 

 

Poll

Poll에서는 Pending에서 발생한 Calback을 실행한다.

중요한 점은 다른 단계와는 달리 특별한 작업이 없으면 대기를 한다는 점이다.

즉, Timer, Pending, Check, Close에서 발생하는 콜백이 있는지 확인을 하고,

작업이 있다면 Check, Close, Timer순으로 다시 Loop가 순환되는 것이고

작업이 없다면 대기하게 된다. 이는 작업이 없는데 단계를 순환하는 것은 비효율적으로 보는 것이다.

 

아래는 Poll 단계의 예시코드이다.

Polling단계에서 특별한 이벤트(connect)가 없다면 Event Loop를 순회하는 것이 비효율적인 것이다.

const net = require('net'); 

const server = net.createServer(socket => { 
	console.log('New connection received:', socket.remoteAddress); 
}); 

server.listen(3000, () => { 
	console.log('Server listening on port 3000'); 
});

 

Check

setImmediate()에 대한 콜백을 수행하는데, Immediate라고 해서 즉시 실행될 것 같지만 사실은 아니다.

 

Close

close()와 같은 콜백을 수행하고 Envent Loop에 작업할 것이 있는지 확인한 뒤,

작업이 없다면, Event Loop를 종료하고

작업이 있다면, 다시 Timer 단계부터 시작한다.

 

 


nextTickQueue와 microTaskQueue

앞서 말한 Event Loop는 Timer, Pending, Idle&prepare, Poll, Check, Close 단계를 순환하는 것을 말하는데

 

단계(Phase)에서 단계로 넘어가는 것을 Tick이라고 한다.

 

이 Tick이 발생할 때 실행되는 콜백이 nextTickQueue와 microTaskQueue이다.

 

nextTickQueue의 경우 process.nextTick()의 함수로 사용되고

 

microTaskQueue는 queueMicrotask(), Promise().then()에서 사용된다.

 

둘이 다른 점은 nextTickQueue가 우선순위가 높다는 것이다.

 

아래는 Event Loop와 nextTck, microTask에 대한 코드와 실행결과이다.

console.log("test start! ", new Date().toString());

setImmediate(() => { console.log("setImmediate", new Date().toString()); });
setTimeout(() => { console.log("setTimeout", new Date().toString()); }, 0);
queueMicrotask(()=>{console.log("microTask", new Date().toString());});
Promise.resolve().then(() => { console.log("Microtask", new Date().toString()); });
process.nextTick(() => { console.log("nextTick", new Date().toString()); });

for(var i = 0; i < 5000000000; i++) {

}

console.log("test end! ", new Date().toString());

 

반응형

'0. 이론' 카테고리의 다른 글

UI 디자이너 UX 디자이너 차이점  (0) 2024.03.18
DNS란?(DNS 구성)  (0) 2024.01.27
Throttle vs Debounce  (0) 2023.01.28
Blocking & Non-blocking I/O + Synchronous&Asynchronous I/O 차이점  (0) 2023.01.10
TCP 송/수신 원리  (0) 2023.01.08

댓글