Node.js는 우리가 브라우저 환경이아닌 백그라운드에서도 Javascript가 동작할 수 있게 해주는 v8기반의 런타임 플랫폼이다
Node는 javascript기반이기에 single thread구조이며 블로킹 작업이 필요할 거 같으면 OS커널에게 처리를 맡겨 OS단에서 처리하도록 지시한다
이렇게 논블로킹방식으로 처리할 수 있게 하는 Node의 핵심기술이 Event Loop이다
Event Loop는 해당 스크립트가 종료되고 실행된다
위의 gif를 확인해보면 분명 Main Execution Context는 종료가 됐지만 2 초 후 Timer의 Execution Context가 실행된걸 알 수 있다
즉 비동기 API나 Timer, nextTick, setImmediate같은 경우는 Main Exeuction Context가 종료되고 난 뒤 Event Loop에 의해서 실행된다
이벤트 루프는 위 다이어그램에 기재된 순서대로 진행된다
각 단계는 FIFO큐를 가지고 있으며 각 단계에서 해당 큐를 전부 소진하거나 일정 시간이 넘어갈 경우 다음 단계로 nextTick이 된다
Timer Phase : 실제 setTimeout이나 setInterval의 실행을 담당하는 단계이다 주의해야 할 부분은 실행을 하는 단계이지 타이밍을 계산하는 단계는 아니다
Pending callback Phase : TCP연결과 관련된 부분인데 사실 중요한 부분은 아니다
Poll Phase : poll은 Callback Queue들을 처리하며 Queue가 비어있을 때 setImmediate가 예약되어있으면 check phase로 넘어가고 예약 되어있지 않으면 Spinning즉 단계를 넘기지 않고 약간의 시간을 더 투자하며 계속 회전상태를 유지한다 오는 즉시 바로 실행하기 위해
또한 Poll Phase에서 Timer의 실행 타이밍을 계산한다
Check Phase : setImmeidate의 실행을 담당한다
Close Phase : socket의 close를 확인하고 open 되어있으면 다음 event loop를 실행한다
사실 위 Event Loop의 Life cycle에는 v8에서의 queue에 대한 내용이 없다 즉 microtask queue를 사용하는 promise같은 경우 node가 아닌 v8 engine이 관리하는 것 이다
따라서 Promise는 언제 실행되는지가 헷갈리는데 이는 node버전에 따라 구븐된다
Node10버전까지는 각 Event Loop가 실행되는 시점에서 microtask와 tick을 실행한 반면 11버전 이후부터 각 phase마다 실행한다
즉 11버전부터 브라우저의 동작방식과 sync가 맞아진 것 이다
위 사진과 같이 각 Phase에서 Tick queue, Microtask queue를 실행하고 이 후 각 phase에 맞는 queue작업을 진행한다
Tick -> Microtask queue -> Tick(Microtask queue) -> Timer Phase의 Callback -> 중간에 Tick(Microtask queue) -> 마지막으로 check phase의 immediate가 실행되는걸 볼 수 있다
즉 Microtask queue는 항상 제일 우선순위로 실행된다
실제 브라우저에서도 마찬가지로 Macrotask queue를 처리하던 도 중 Microtask queue에 값이 적재될 시 Microtask를 먼저 실행된다
위의 queue우선 순위에서 봤었던것 처럼 Node또한 각 Phase(Macrotask queue)를 실행하기전 혹은 실행도중 Microtask에 값이 적재될 시 이를 가져다가 우선적으로 사용한다
그러나 Node10이전 버전은 Event Loop가 새롭게 시작되는 즉 Close Phase -> Timer Phase의 사이에서만 실행 됐으므로 이 때는 브라우저와 다른 결과값을 볼 수 있다