이 글은 Node의 이벤트 루프 분석을 소개합니다. 필요한 친구들이 참고할 수 있습니다.
Node.js의 이벤트 루프 프로세스는 대략 다음과 같습니다.
┌───────────────────────────┐ ┌─>│ timers │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ pending callbacks │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ │ └─────────────┬─────────────┘ ┌───────────────┐ │ ┌─────────────┴─────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └─────────────┬─────────────┘ │ data, etc. │ │ ┌─────────────┴─────────────┐ └───────────────┘ │ │ check │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ close callbacks │ └───────────────────────────┘
각 단계에는 자체 작업 대기열이 있습니다. 이 단계의 작업 대기열이 완료되거나 실행되는 최대 작업은 다음과 같습니다. 숫자에 도달하면 다음 단계로 진입하게 됩니다.
이 단계에서는 setTimeout
및 setInterval
에 의해 설정된 예약된 작업을 실행합니다.
물론 이 타이밍은 정확하지 않으나, 타이밍 시간을 초과한 후 실행 기회를 얻으면 즉시 실행됩니다. setTimeout
和 setInterval
设置的定时任务。
当然,这个定时并不是准确的,而是在超过了定时时间后,一旦得到执行机会,就立刻执行。
这个阶段会执行一些和底层系统有关的操作,例如TCP连接返回的错误等。这些错误发生时,会被Node 推迟到下一个循环中执行。
这个阶段是用来执行和 IO 操作有关的回调的,Node会向操作系统询问是否有新的 IO 事件已经触发,然后会执行响应的事件回调。几乎所有除了 定时器事件、 setImmediate()
和 close callbacks
之外操作都会在这个阶段执行。
这个阶段会执行 setImmediate()
设置的任务。
如果一个 socket
或 handle(句柄)
突然被关闭了,例如通过 socket.destroy()
关闭了,close
事件将会在这个阶段发出。
事件循环初始化之后,会按照上图所示的流程进行:
首先会依次执行 定时器中的任务、 pending callback
回调;
然后进入到 idle
、 prepare
阶段,这里会执行 Node 内部的一些逻辑;
然后进入到 poll
轮询阶段。在这个阶段会执行所有的 IO 回调,如 读取文件,网络操作等。 poll
阶段有一个 poll queue
任务队列。这个阶段的执行过程相对较长,具体如下:
进入到本阶段,会先检查 timeout
定时队列是否有可执行的任务,如果有,会跳转到 定时器阶段
执行。
如果没有 定时器任务
,就会检查 poll queue
任务队列,如果不为空,会遍历执行所有任务直到都执行完毕或者达到能执行的最大的任务数量。
poll queue
任务队列执行完成后,会检查 setImmediate
任务队列是否有任务,如果有的话,事件循环会转移到下一个 check
阶段。
如果没有 setImmediate
任务,那么,Node 将会在此等待,等待新的 IO 回调的到来,并立刻执行他们。
注意 :这个等待不会一直等待下去,而是达到一个限定条件之后,继续转到下一个阶段去执行。
setTimeout()
和 setImmediate()
其实也不算秘密,只是我是在刚刚查阅资料才知道的。
那就是:在 Node 中,setTimeout(callback, 0)
会被转换为 setTimeout(callback, 1)
。
详情请参考 这里 。
setTimeout()
和 setImmediate()
的执行顺序下面这两个定时任务执行的顺序在不同情况下,表现不一致。
setTimeout(function() { console.log('timeout'); }, 0); setImmediate(function() { console.log('immediate'); });
如果在普通的代码执行阶段(例如在最外层代码块中),设置这两个定时任务,他们的执行顺序是不固定的。
首先,我们设置的 setTimeout(callback, 0)
已经被转换成为 setTimeout(callback, 1)
,所以进入 定时器
阶段时,会根据当前时间判断定时是否超过了 1ms
。
事件循环在进入定时器阶段之前会由系统调用方法来更新当前时间,由于系统中同时运行着其他的程序,系统需要等待其他程序的进程运行结束才能获取准确时间,所以更新得到的时间可能会有一定的延迟。
更新时间时,若没有延迟,定时不到 1ms
,immediate
任务会先执行;如果存在延迟,并且这个时间达到了 1ms
的界限, timeout
任务就会首先执行。
如果我们在 IO 回调中设置了这两个定时器,那么 setImmediate
setImmediate()
및 콜백 닫기
를 제외한 거의 모든 작업이 이 단계에서 수행됩니다. 🎜🎜check 단계🎜🎜이 단계에서는 setImmediate()
에 의해 설정된 작업이 실행됩니다. 🎜🎜콜백 닫기 단계🎜🎜 socket
또는 handle(handle)
이 예를 들어 socket.destroy()
를 통해 갑자기 닫히면 이 단계에서 close
이벤트가 발생합니다. 🎜🎜이벤트 루프의 구체적인 실행🎜🎜이벤트 루프가 초기화된 후에는 위 그림의 프로세스에 따라 진행됩니다. 🎜대기 중인 콜백
콜백 🎜idle
및 prepare
를 입력하여 순차적으로 실행됩니다. Node의 일부 내부 프로세스가 실행되는 단계입니다. 🎜poll
폴링 단계로 들어갑니다. 이 단계에서는 파일 읽기, 네트워크 작업 등과 같은 모든 IO 콜백이 실행됩니다. poll
단계에는 poll queue
작업 대기열이 있습니다. 이 단계의 실행 과정은 다음과 같이 비교적 길다: 🎜timeout<이 발생합니다. 먼저 /code> 확인 타이밍 큐에 실행 가능한 작업이 있는지 여부. 그렇다면 실행을 위해 <code>타이머 단계
로 점프합니다. 🎜타이머 작업
이 없으면 폴링 대기열
작업 대기열이 비어 있지 않으면 모든 작업이 순회됩니다. 모두 완료되거나 수행할 수 있는 최대 작업 수에 도달할 때까지 실행됩니다. 🎜poll queue
작업 대기열이 실행된 후 setImmediate
작업 대기열에 작업이 있는지 확인합니다. 루프는 다음 check
단계로 전환됩니다. 🎜setImmediate
작업이 없으면 노드는 여기에서 새 IO 콜백이 도착할 때까지 기다렸다가 즉시 실행합니다. 🎜setTimeout()
및 setImmediate()
🎜🎜작은 비밀🎜🎜사실 비밀은 아니지만 정보를 확인하다가 알게 됐어요. setTimeout(callback, 0)
이 setTimeout(callback, 1)
로 변환됩니다. setTimeout()
및 setImmediate()
의 실행 순서 🎜🎜다음 두 예약 작업의 실행 순서는 상황에 따라 성능이 일관되지 않습니다. 🎜setTimeout(function() { setImmediate(() => { console.log('immediate'); }); process.nextTick(() => { console.log('nextTick'); }); }, 0); // nextTick // immediate
setTimeout(callback, 0)
이 setTimeout(callback, 1)으로 변환되었습니다.
code>이므로 Timer
단계에 들어갈 때 타이밍이 1ms
를 초과하는지 여부를 현재 시간을 기준으로 판단합니다. 🎜1ms
미만인 경우 immediate
작업이 먼저 실행됩니다. 지연이 있고 이 시간이 도달합니다. 1ms
제한을 초과하면 timeout
작업이 먼저 실행됩니다. 🎜setImmediate
작업이 먼저 실행됩니다. 다음 이유: 🎜进入 poll phase
轮询阶段之前会先检查是否有 timer
定时任务。
如果没有 timer
定时任务,才会执行后面的 IO 回调。
我们在 IO 回调中设置 setTimeout
定时任务,这时已经过了 timer
检查阶段,所以 timer
定时任务会被推迟到下一个循环中执行。
process.nextTick()
无论在事件循环的哪个阶段,只要使用 process.nextTick()
添加了回调任务,Node 都会在进入下一阶段之前把 nextTickQueue
队列中的任务执行完。
setTimeout(function() { setImmediate(() => { console.log('immediate'); }); process.nextTick(() => { console.log('nextTick'); }); }, 0); // nextTick // immediate
上述代码中,总是先执行 nextTick
任务,就是因为在循环在进入下一个阶段之前会先执行 nextTickQueue
中的任务。下面代码的执行结果也符合预期。
setImmediate(() => { setTimeout(() => { console.log('timeout'); }, 0); process.nextTick(() => { console.log('nextTick'); }); }); // nextTick // timeout
相关推荐:
위 내용은 Node의 이벤트 루프 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!