공부한 내용/javascript

이벤트 루프란?

hyongti 2022. 2. 23. 13:20
이 글은 2018년 2월 20일 Paul Shan이 작성한 Node.js event loop workflow & lifecycle in low level을 번역한 글입니다. 의역이 있을 수 있습니다.
- 제가 번역하면서 (공부한 내용)도 각 문단의 뒤에 링크로 추가해놨습니다.
- 번역 투가 마음에 안 들지만.. 잘 몰라서 더 잘 번역할 수가 없었습니다 ㅠㅠ

1년 전 setImmediate & process.nextTick의 차이점을 설명하면서 노드 이벤트 루프의 저수준 아키텍처에 대해 약간 썼습니다.
놀랍게도 그 게시물의 독자들은 나머지 부분보다 이벤트 루프 부분에 더 관심을 갖게 되었고, 나는 그 부분에 대한 질문을 많이 받았습니다.
그래서 node.js 이벤트 루프의 저수준 workflow에 대한 큰 그림을 그리기로 결정했습니다.

 

각 문단마다 몇 가지 훌륭한 정보가 있으므로 글머리 기호 뿐만 아니라 전체 기사를 읽는 것이 좋습니다!

왜 이 글을  작성하게 되었나?

내가 node.js 이벤트 루프에 대해 구글링한다면, 거기에 있는 대부분의 글은 큰 그림을 설명하지 않습니다(그들은 매우 높은 수준의 추상화로 설명하려고 합니다).

node.js 이벤트 루프를 이용한 구글 이미지 검색 스크린샷입니다. 그리고 여기에 있는 이미지 결과의 대부분은 잘못되었거나 실제 이벤트 루프를 대해 매우 추상화한 이미지입니다.

 

이러한 설명들로 인해 개발자는 이벤트 루프에 대한 오해를 가지곤 합니다. 다음은 매우 일반적인 오해 중 일부입니다.

몇 가지 일반적인 오해

이벤트 루프는 JS 엔진 내부에 있다.

매우 일반적인 오해 중 하나는 이벤트 루프가 JavaScript 엔진(v8, spiderMonkey 등)의 일부라는 것입니다. 실제로 이벤트 루프는 JavaScript 엔진을 사용하여 JavaScript 코드를 실행하는 주체입니다.(실제 이벤트 루프는 node.js나 브라우저에 있음)

이벤트 루프는 싱글 스택 또는 큐로 동작한다.

우선 이벤트 루프에는 스택이 없습니다. 둘째, 프로세스가 복잡하고 여러 큐(자료 구조의 queue)를 가지고 있습니다. 그러나 대부분의 개발자는 모든 콜백이 하나의 큐에 의해 실행된다고 알고 있습니다. 이건 완전히 잘못된 생각입니다.(queue, stack이란?)

이벤트 루프는 여러개의 스레드에서 실행된다.

node.js의 잘못된 이벤트 루프 다이어그램으로 인해 우리 중 일부(저는 JavaScript 초기에 그들 중 하나였습니다)가 두 개의 스레드가 있다고 알고 있습니다. 하나는 JavaScript를 실행하고 다른 하나는 이벤트 루프를 실행합니다. 그러나 실제로는 모든 것이 단일 스레드에서 동작합니다. 

setTimeout이 일부 비동기 OS API와 관련이 있다.

또 다른 큰 오해는 주어진 지연이 완료될 때 setTimeout의 콜백이 다른 실행 주체(OS 또는 커널일 수도 있는)에 의해 큐에 푸시된다는 것입니다. 하지만 다른 실행 주체는 없습니다. 우리는 곧 메커니즘에 대해 논의할 것입니다.

setImmediate는 콜백을 0번째 위치에 배치합니다.

일반적인 이벤트 루프 설명에는 큐가 하나만 있습니다. 따라서 일부 개발자는 setImmediate()가 작업 큐의 앞에 콜백을 배치한다고 생각합니다. 이것은 완전히 잘못된 생각이며, JavaScript의 모든 작업 큐는 FIFO(선입 선출)입니다.

이벤트 루프의 아키텍쳐

이벤트 루프의 workflow를 설명하기 전에 이벤트 루프의 아키텍처를 아는 것이 중요합니다. 위에서 말했듯이 큐와 순환하는 동그라미가 있는 작은 그림(대부분의 잘못된 그림)은 이벤트 루프를 제대로 설명하지 못합니다. 아래는 이벤트 루프의 단계 별 이미지입니다.

위 이미지의 각 상자는 특정 작업을 수행하기 위한 전용 단계(phase)를 나타냅니다. 각 단계에는 큐(더 쉬운 이해를 위해 큐라고 했지만, 실제 자료 구조는 큐가 아닐 수 있음)가 연결되어 있으며, JavaScript 실행은 이러한 단계(idle, prepare 제외)에서 수행될 수 있습니다.

 

또한 그림 중간에 있는 nextTickQueuemicroTaskQueue를 볼 수 있습니다. 이는 실제로 루프의 일부가 아니며 두 큐 내부의 콜백은 이벤트 루프의 모든 단계에서 실행될 수 있고, 가장 높은 실행 우선 순위를 가지고 있습니다.

 

이제 여러분은 이벤트 루프가 실제로 다른 큐와 다른 단계의 조합이라는 것을 알고 있습니다. 다음은 각 단계에 대한 설명입니다.

Timer 단계

이것은 이벤트 루프의 시작 단계입니다. 이 단계에 연결된 이 큐는 타이머(예: setTimeout, setInterval) 콜백을 보유합니다. 실제로 큐에 콜백을 푸시하지 않지만 min-heap에 타이머를 유지다가 타이머가 경과된 콜백을 실행합니다.(min-heap이란?)

Pending i/o callback 단계

이 단계는 이벤트 루프의 pending_queue에 있는 콜백을 실행합니다. 이러한 종류의 콜백은 이전 작업에서 푸시됩니다. 예를 들어, 당신이 TCP 핸들러에 무언가를 작성하려고 하고 작업이 완료되면 콜백이 이 대기열에 푸시됩니다. 에러 콜백도 여기에서 찾을 수 있습니다.(tcp 핸들러란?)

Idle, Prepare 단계

이름은 idle이지만 이 단계는 각 tick에서 실행됩니다. prepare 단계도 polling이 시작될 때마다 실행됩니다. 어쨌든 이것은 node.js의 내부 작업을 위한 두 단계입니다. 그래서 우리는 여기서 논의하지 않습니다.

Poll 단계

전체 이벤트 루프의 가장 중요한 단계는 아마도 poll phase일 것입니다. 이 단계는 새로운 들어오는 연결(새 소켓 설정 등)과 데이터(파일 읽기 등)를 수락합니다. poll 작업을 몇 가지 다른 부분으로 나눌 수 있습니다.

  • watch_queue(폴링 단계에 연결된 큐)에 무언가가 있는 경우, 큐가 빌 때까지 또는 시스템 별 최대 실행 한도에 도달할 때까지 차례로 동기적으로 실행됩니다.
  • 큐가 비어 있으면 node.js는 새로운 연결 등을 기다립니다. node.js의 대기 시간은 다양한 요인에 따라 계산되며 아래에서 이에 대해 알아볼 것입니다.

Check 단계

polld의 다음 단계는 setImmediate() 콜백 전용 단계인 check 단계입니다. 이 단계에 대한 일반적인 질문은 왜 setImmediate 콜백에 대해 별도의 큐가 존재하는지입니다. 이는 워크플로 섹션에서 논의할 poll 단계의 동작 때문이기도 합니다. 그때까지는 check 단계가 setImmediate() API의 콜백 전용임을 기억하십시오.

Close callbacks 단계

closee 이벤트 타입의 콜백(socket.on('close', ()=>{}))은 여기에서 처리됩니다. cleanup 단계라고 할 수 있습니다.

nextTickQueue & microTaskQueue

nextTickQueue의 태스크는 api process.nextTick()을 사용하여 호출된 콜백을 보유하고 microTaskQueue는 resolved promises에 의해 호출된 콜백을 보유합니다. 이 두 가지는 실제로 이벤트 루프의 일부가 아닙니다. 즉, libUV 라이브러리 내부에 있는 것이 아니라 node.js내부의 기술입니다. C/C++와 JavaScript의 경계를 넘을 때마다 가능한 한 빨리 호출됩니다. 따라서 현재 실행 중인 작업 직후에 호출되어야 합니다(현재 실행 중인 JS 함수 콜백일 필요는 없음)(libuv란?)


이벤트 루프의 workflow

콘솔에서 node my-script.js를 실행하면 node는 이벤트 루프를 설정한 다음, 이벤트 루프 외부에서 메인 모듈(my-script.js)을 실행합니다. 메인 모듈이 실행되면 노드는 루프가 활성 상태인지 즉, 이벤트 루프에서 할 일이 있는지 확인합니다. 이벤트 루프에서 할 일이 없는 경우 종료 콜백, 다시 말하면 process.on('exit', foo)와 같은 콜백을 실행하고 이벤트 루프를 종료하려고 할 것입니다.

만약 루프가 활성 상태이면 node.js는 타이머 단계로 루프에 들어갑니다.

Timer 단계의 workflow

이벤트루프가 Timer 단계에 들어가게 되면, 타이머 큐에 실행할 항목이 있는지 확인합니다. 이 문장은 매우 간단하게 들릴지 모르지만, 이벤트 루프는 실제로 적절한 콜백을 찾기 위해 몇 가지 단계를 수행해야 합니다. 실제로 타이머 스크립트는 힙 메모리에 오름차순으로 저장됩니다. 이벤트 루프는 타이머 스크립트를 까보면서 now - registeredTime == delta인지 계산할 것입니다. 만약 true라면, 해당 타이머의 콜백을 실행하고 다음 타이머를 확인합니다. 시간이 경과하지 않은 타이머를 찾을 수 없을 때마다 다른 타이머 확인을 중지하고(타이머는 오름차순으로 정렬됨) 다음 단계로 이동합니다. 만약 false인 타이머를 만나면, 다른 타이머를 확인하지 않고 다음 단계로 넘어갑니다(타이머들이 오름차순으로 정렬되어 있기 때문).

 

임의의 시간 t에서 setTimeout을 4번 호출하여 시간 값이 100, 200, 300400ms인 4개의 타이머(A, B, C, D)를 생성했다고 가정해봅시다.

이벤트 루프가 시간 t+250에서 타이머 단계에 들어갔다고 가정합니다. 먼저 타이머 A를 찾고 만료 시간이 t+100임을 알 수 있습니다. 그러나 지금 시간은 이미 t+250입니다. 따라서 타이머 A에 연결된 콜백을 실행합니다. 그런 다음 B 타이머를 확인하고 t+200 역시 시간이 만료되었으므로 타이머 B에 연결된 콜백 역시 실행합니다. 이제 C를 확인하고 경과 시간이 t+300이므로 그대로 둡니다. 타이머가 오름차순으로 정렬되었기 때문에 이벤트 루프는 D를 확인하지 않습니다. 따라서 D의 만료 시간은 C보다 클 수밖에 없습니다.


그러나 각 단계에는 시스템의 Hard Limit가 있으므로 실행되지 않은 타이머가 경과하더라도 시스템 종속 Max Limit에 도달하면 다음 단계로 넘어갑니다.

Pending i/o 단계의 workflow

타이머 단계 후에 이벤트 루프는 pending i/o 단계로 들어가 이전 작업의 일부 콜백이 pending_queue에 있는지 여부를 확인합니다. 보류 중인 경우 pending_queue가 빌 때까지 또는 시스템의 실행 한도 초과에 도달할 때까지 차례로 실행됩니다.

그 후 이벤트 루프는 idle handler단계로 이동하고 내부 작업을 수행하기 위한 prepare 단계가 이어지며 마지막으로 가장 중요한 단계인 poll 단계로 이동합니다.

Poll 단계의 workflow

이름에서 알 수 있듯이, 새로운 들어오는 요청이나 연결이 만들어졌는지 지켜보는 단계입니다.

이벤트 루프가 poll 단계에 들어가면 watcher_queue 내부에 있는 파일 읽기 응답, 새 소켓 또는 http 연결 요청과 같은 스크립트를 실행합니다. 다른 모든 queue와 마찬가지로, watcher_queue가 비거나, 시스템의 실행 한도 초과에 도달할 때까지 차례로 실행됩니다.

실행할 콜백이 더 이상 없는 경우 poll은 특정 조건을 가지고 일정 시간 대기합니다.

 

check queue, pending queue 또는 close callbacks queue(idle handler queue도 마찬가지)에 보류 중인 작업이 있는 경우 대기하지 않습니다. 만약 보류 중인 작업이 없다면, 대기 시간을 결정하기 위해 타이머 힙에서 첫 번째 타이머(만약 타이머가 있다면)를 실행합니다. 첫 번째 타이머의 만료 시간이 지나가면 다음 단계로 넘어갑니다.

Check 단계의 workflow

poll 단계 후 이벤트 루프는 즉시 setImmediate() api에 의해 호출된 콜백이 있을 수 있는 큐의 단계를 확인하기 위해 내려옵니다. 큐가 비거나 시스템의 실행 한도에 도달할 때까지 다른 단계와 마찬가지로 동기적으로 콜백을 실행합니다.

Close callback workflow

check 단계에서 작업을 완료한 후 이벤트 루프의 다음 목적지는 close 또는 destroy 타입의 콜백을 처리하는 Close callback입니다.

close callback를 실행하며 이벤트 루프가 종료된 후, 이벤트 루프는 루프가 살아 있는지 다시 확인합니다. 그렇지 않은 경우 단순히 종료됩니다. 그러나 무언가가 있다면 다음 반복으로 넘어가서 Timer 단계를 시작할 것입니다.

nextTickQueue & microTaskQueue

그렇다면 이 두 큐의 콜백은 언제 실행될까요? 현재 단계에서 다음 단계로 이동하기 전에 가능한 한 빨리 그리고 확실히 실행됩니다. 다른 단계와 달리 이 두 단계는 시스템의 실행 한도가 없으며 nodejs는 두 큐가 완전히 비워질 때까지 실행합니다. nextTickQueue는 microTaskQueue보다 우선 순위가 높습니다.

Thread-pool

JavaScript 개발자들로부터 가장 많이 듣는 단어는 ThreadPool입니다. 그리고 매우 일반적인 오해는 node.js에 모든 비동기 작업을 처리하는 데 사용되는 스레드 풀이 있다는 것입니다. 그러나 사실 스레드 풀은 nodejs에서 비동기 작업을 처리하기 위해 사용하는 libUV 라이브러리에 있는 것입니다.

이벤트 루프 메커니즘의 일부가 아니기 때문에 이벤트 루프 다이어그램에 이것을 표시하지 않았습니다. 아마도 libUV에 대한 별도의 게시물에서 설명할 수 있을 것입니다. 당분간은 모든 비동기 작업이 스레드 풀에서 처리되지 않는다는 점을 말씀드리고 싶습니다. LibUV는 OS의 비동기 API를 사용하여 event driven 환경을 유지할 만큼 충분히 똑똑합니다. 그러나 파일 읽기, DNS 조회 등과 OS 커널이 비동기 API를 지원하지 않는 경우, 기본적으로 4개의 스레드만 사용하는 스레드 풀에서 처리됩니다. uv_threadpool_size 환경 변수를 사용하여 스레드 크기를 128까지 늘릴 수 있습니다.


Workflow with examples

이제 이벤트 루프가 어떻게 작동하는지에 대한 아이디어를 얻으셨기를 바랍니다. C 언어의 동기식 while 루프로 어떻게 JavaScript가  비동기식으로 작동하도록 돕는지 말이죠. 이 구조는 한 번에 한 가지만 실행하지만 여전히 거의 차단되지 않습니다.

 

어쨌든, 우리가 이론을 아무리 잘 설명하더라도, 예시를 본다면 더 잘 이해할 수 있을 것입니다. 따라서 몇 가지 코드 스니펫으로 시나리오를 이해해 보겠습니다.

 

Snippet 1 - basic understanding

setTimeout(() => {
    console.log('setTimeout');
}, 0);
setImmediate(() => {
  console.log('setImmediate');
});

 

위의 결과를 추측할 수 있습니까? setTimeout이 먼저 인쇄될 것이라고 생각할 수 있지만, 그것이 보장된 것은 아닙니다. 왜요? 메인 모듈을 실행한 후 타이머 단계에 들어갈 때 타이머의 시간이 경과된 것을 발견할 수도, 그러지 못할 수도 있기 때문입니다.

왜 그럴까요? 타이머 스크립트는 사용자가 제공하는 시스템 시간과 델타 시간으로 등록되기 때문입니다. 이제 setTimeout이 호출되고 타이머 스크립트가 메모리에 기록되는 순간은 머신의 성능과 머신에서 실행 중인 다른 작업(nodejs 아님)에 따라 약간의 지연이 있을 수 있습니다.

 

또 다른 요점은 nodejs가 타이머 단계에 들어가기 직전에 변수 now를 설정하고(각 반복마다) now를 현재 시간으로 간주한다는 것입니다. 따라서 정확한 계산이라기엔 약간의 버그가 있다고 말할 수 있고, 이것이 이러한 불확실성의 이유입니다. 타이머 API(예: setTimeout)의 콜백 내에서 동일한 코드를 실행하려고 하면 비슷한 일이 예상됩니다.

 

그러나 I/O cycle 내부로 이 코드를 옮기면 setTimeout보다 앞서 실행되는 setImmediate 콜백이 보장됩니다.

fs.readFile('my-file-path.txt', () => {
  setTimeout(() => {
    console.log('setTimeout');
  }, 0);
  setImmediate(() => {
    console.log('setImmediate');
  });
});

(파일 읽기, 쓰기는 비동기 작업이므로 비동기 작업의 처리는 libuv가 담당합니다. libuv는 os 커널에서 제공하는 비동기 api로 비동기 작업을 처리합니다. 하지만 파일 읽기는 os커널에서 비동기 api를 제공하지 않기에 libuv의 쓰레드풀이 아닌 쓰레드에 작업을 위임합니다. 파일 읽기가 완료되면 해당 콜백은 pending i/o 단계의 pending_queue에 등록됩니다. 콜백은 이벤트루프가 pending i/o 단계에 있을 때 실행될 것입니다. 콜백 내부의 setTimeout의 콜백은 Timer 단계의 큐에 등록되고, setImmediate의 콜백은 Check 단계의 큐에 등록됩니다. poll 단계에서는 check 단계의 큐에 작업이 있으므로 check 단계로 가서 큐의 작업을 수행하고, Timer 단계의 큐에 작업이 있으므로 이벤트 루프는 종료되지 않고 Timer 단계로 넘어가 콜백을 실행합니다.)

Snippet 2 - understanding timers better

var i = 0;
var start = new Date();
function foo () {
    i++;
    if (i < 1000) {
        setImmediate(foo);
    } else {
        var end = new Date();
        console.log("Execution time: ", (end - start));
    }
}
foo();

위의 예시는 매우 간단합니다. foo 함수는 1000의 한계까지 재귀적으로 setImmediate()를 사용하여 호출됩니다. 노드 버전 8.9.1에서는 실행되는 데 6~8ms가 걸립니다.

 

이제 위의 스니펫에서 setImmediate(foo)를 setTimeout(foo, 0)으로 변경해 보겠습니다.

var i = 0;
var start = new Date();
function foo () {
    i++;
    if (i < 1000) {
        setTimeout(foo, 0);
    } else {
        var end = new Date();
        console.log("Execution time: ", (end - start));
    }
}
foo();

이제 똑같은 환경에서 위 코드를 실행하면 실행되는 데 1400+ms가 걸립니다.

 

왜 그럴까요? I/O 이벤트가 없기 때문에 매우 동일해야 할 것입니다. 두 경우 모두 폴링 대기 시간은 0입니다. 그런데도 왜 이렇게 시간이 많이 걸리는 걸까요?

 

왜냐하면 시간을 비교해서 차이를 찾는 것은 CPU 집약적인 작업이고 시간이 더 오래 걸리기 때문입니다.

 

타이머 스크립트를 등록하는 데도 시간이 걸립니다. 각 지점에서 타이머 단계는 타이머가 경과하고 콜백을 실행해야 하는지 여부를 결정하기 위해 몇 가지 작업을 거쳐야 합니다. 실행 시간이 길수록 더 많은 틱이 발생할 수도 있습니다. 그러나 setImmediate의 경우 검사가 없습니다. 콜백이 대기열에 있으면 실행하는 것과 같습니다.

Snippet 3 - understanding nextTick() & timer execution

var i = 0;
function foo(){
  i++;
  if(i>20){
    return;
  }
  console.log("foo");
  setTimeout(()=>{
    console.log("setTimeout");
  },0);
  process.nextTick(foo);
}   
setTimeout(foo, 2);

위 함수의 출력은 어떻게 되어야 한다고 생각하십니까? 예, 먼저 모든 foo를 인쇄한 다음 setTimeouts를 인쇄합니다. 2ms 후에 첫 번째 foo가 인쇄되어 nextTickQueue에서 재귀적으로 foo()를 호출합니다. 모든 nextTickQueue 콜백이 실행되면 setTimeout 콜백과 같은 다른 콜백을 처리합니다.

 

그러면 각 콜백 실행 후에 nextTickQueue가 확인되는 것과 같습니까? 코드를 약간 수정하여 살펴보겠습니다.

var i = 0;
function foo(){
  i++;
  if(i>20){
    return;
  }
  console.log("foo", i);
  setTimeout(()=>{
    console.log("setTimeout", i);
  },0);
  process.nextTick(foo);
}

setTimeout(foo, 2);
setTimeout(()=>{
  console.log("Other setTimeout");
},2);

기존 setTimeout과 동일한 시간이 경과된 뒤에 Other setTimeout을 출력하는 다른 setTimeout을 추가했습니다. 보장되지는 않지만 한 번의 foo 인쇄 후에 Other setTimeout이 출력될 수 있습니다. 타이머들이 들어있는 힙 내부에 동일한 딜레이를 가진 타이머들은 어떻게든 그룹화되어있고, nextTickQueue의 체크는 진행 중인 콜백 그룹의 실행이 끝난 후에야 진행되기 때문입니다.


몇 가지 일반적인 질문들

자바스크립트는 어디에서 실행되나요?

우리 중 많은 사람들이 이벤트 루프가 별도의 스레드에서 돌아가면서 큐에 콜백을 푸시하고 해당 큐에서 하나씩 콜백이 실행된다는 것을 이해했습니다. 사람들은 이 게시물을 처음 읽을 때 정확히 JavaScript가 실행되는 위치를 혼동할 수 있습니다.

 

앞서 말했듯이 이벤트 루프 자체에서 v8(또는 기타) 엔진을 사용하여 자바스크립트를 실행합니다. 이때 단 하나의 스레드를 사용합니다. 실행은 완전히 동기적이며 현재 JavaScript 실행이 완료되지 않으면 이벤트 루프가 진행되지 않습니다.

setTimeout(fn, 0)이 있는데 setImmediate가 필요한 이유는 무엇인가요?

우선 이것은 0이 아닙니다. 1입니다. 타이머를 1보다 작거나 2147483647ms보다 큰 값으로 설정할 때마다 자동으로 1로 설정됩니다. 따라서 SetTimeout을 0으로 설정하려고 할 때마다 1이 됩니다.

 

setImmediate는 이미 논의한 것처럼 추가 검사(ex. cur - now === delta)의 골칫거리를 줄여줍니다. 따라서 setImmediate는 작업을 더 빠르게 만듭니다. 또한 폴 단계 직후에 배치되므로 새로운 수신 요청에서 호출된 모든 setImmediate 콜백이 곧 실행됩니다.

왜 setImmediate가 immediate라고 불리는 건가요?

음, setImmediate와 process.nextTick 모두 이름이 잘못되었습니다. 실제로 setImmediate 단계는 틱 또는 루프를 돌때 마다 관리되고 nextTick은 가능한 한 빨리(단계를 넘어갈 때마다) 호출됩니다. 따라서 기능적으로 setImmediate는 nextTick이고 nextTick은 immediate입니다. 😛

JavaScript는 block 될 수 있나요?

이미 보았듯이 nextTickQueue에는 콜백 실행 제한이 없습니다. 따라서 재귀적으로 process.nextTick()을 호출하면 다른 단계에서 가지고 있는 모든 콜백에 관계없이 프로그램이 절대 빠져나오지 않습니다.

exit callback 단계에서 setTimeout을 호출하면 어떻게 되나요?

타이머를 시작할 수 있지만 콜백은 호출되지 않습니다. node.js가 exit callbacks에 들어가 있으면 이미 이벤트 루프에서 나온 것입니다. 

 


짧은 정리들

  • 이벤트 루프는 작업 스택을 가지고 있지 않습니다.
  • 자바스크립트 실행은 이벤트루프 안에서 이뤄집니다. 이벤트루프가 별도의 스레드에서 실행되고 자바스크립트를 실행은 어떤 큐에서 콜백을 가져와서 실행하는 것이 아닙니다.
  • setImmediate는 작업 큐의 앞에 콜백을 푸시하는 것이 아니고 전용 단계와 큐가 있습니다.
  • setImmediate는 다음 틱에서 실행되고 nextTick은 실제로 즉각적입니다.
  • nextTickQueue는 재귀적으로 호출되는 경우 노드를 블록할 수 있으므로 주의하세요.

 

마치며

저는 핵심 node.js 개발 팀에 있지 않습니다. 이 포스팅에 대한 나의 모든 지식은 다양한 대화와 기사 및 실험에서 얻은 것입니다. 내가 이것에 대해 처음 알게 해준 node.js 문서와 두 번째로 libUV에 대한 강연을 해주신 Saúl Ibarra Corretgé에게 감사드립니다.
세 번째이자 가장 중요한 것은 사물을 이해하고 삶을 더 단순하게 만들기 위해 많은 건전한 토론과 실험/예를 만든 VoidCanvas 독자 덕분입니다. :)


아직 이벤트 루프와 libuv의 관계, 이벤트 기반, 논 블로킹 I/O, 싱글 스레드 등이 정확히 무슨 말인지 몰라서 더 참고해야 할 블로그도 찾아놓았습니다.

[node.js] node.js의 이벤트루프와 libuv의 이해

 

Row level Node : js의 동작 방식부터 libuv와 event loop까지

nodejs의 내부 동작 원리(libuv, 이벤트루프, 워커쓰레드, 비동기)

(영상)Philip Roberts: Help, I'm stuck in an event-loop

브라우저의 이벤트 루프가 궁금해서 찾아본,

이벤트 루프, 넌 누구냐