이 글의 내용은 Node.js의 이벤트 루프 메커니즘을 분석하는 것입니다. 참고할만한 가치가 있으니 참고하시면 도움이 될 것입니다.
이벤트 루프 메커니즘과 일부 관련 개념은 브라우저 기사에서 자세히 소개되었지만 주로 브라우저 측에 대한 연구를 위한 것입니다. Node 환경에서도 마찬가지인가요? 먼저 데모를 살펴보겠습니다.
setTimeout(()=>{ console.log('timer1') Promise.resolve().then(function() { console.log('promise1') })}, 0)setTimeout(()=>{ console.log('timer2') Promise.resolve().then(function() { console.log('promise2') })}, 0)
육안으로 컴파일하고 실행해 보면 다음과 같습니다. 여러분은 이미 사실을 알고 있으므로 자세한 내용은 다루지 않겠습니다. .
timer1 promise1 timer2 promise2
그럼 Node에서 실행해 보세요. . . 이상하네요 브라우저와 실행 결과가 다릅니다~
timer1 timer2 promise1 promise2
예제를 보면 브라우저와 Node.js의 이벤트 루프 메커니즘이 다르다는 것을 알 수 있는데 살펴보겠습니다~
#🎜🎜 #Node.js 이벤트 처리Node.js는 V8을 js 구문 분석 엔진으로 사용하고 I/O 처리를 위해 자체적으로 설계된 libuv를 사용합니다. libuv는 이벤트 기반 크로스-플랫폼 추상화 계층이 캡슐화됩니다. 다양한 운영 체제의 일부 기본 기능을 제공하며 이벤트 루프 메커니즘도 여기에 구현되어 있습니다. 핵심 소스 코드 참조:int uv_run(uv_loop_t* loop, uv_run_mode mode) { int timeout; int r; int ran_pending; r = uv__loop_alive(loop); if (!r) uv__update_time(loop); while (r != 0 && loop->stop_flag == 0) { uv__update_time(loop); // timers阶段 uv__run_timers(loop); // I/O callbacks阶段 ran_pending = uv__run_pending(loop); // idle阶段 uv__run_idle(loop); // prepare阶段 uv__run_prepare(loop); timeout = 0; if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) timeout = uv_backend_timeout(loop); // poll阶段 uv__io_poll(loop, timeout); // check阶段 uv__run_check(loop); // close callbacks阶段 uv__run_closing_handles(loop); if (mode == UV_RUN_ONCE) { uv__update_time(loop); uv__run_timers(loop); } r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) break; } if (loop->stop_flag != 0) loop->stop_flag = 0; return r; }
poll 단계: 새로운 I/O 이벤트를 가져오면 노드는 적절한 조건에서 여기에서 차단됩니다
check 단계: setImmediate() 콜백 실행
#🎜 🎜 #close 콜백 단계: 소켓의 닫기 이벤트 콜백 실행 타이머, 폴링, 확인의 세 단계에 집중하겠습니다. 일상적인 개발에서 대부분의 비동기 작업은 이 세 단계에서 처리되기 때문입니다. 단계.timers 단계
timers는 이벤트 루프의 첫 번째 단계인 노드입니다. 만료된 타이머가 있는지 확인하고, 그렇다면 타이머의 작업 대기열에 콜백을 푸시하여 실행을 기다립니다. 노드의 타이머 만료 확인이 반드시 신뢰할 수 있는 것은 아니기 때문에 미리 설정된 시간에 도달하면 타이머가 즉시 실행된다는 보장은 없습니다. 머신에서 실행 중인 다른 프로그램의 영향을 받거나 메인 스레드가 유휴 상태가 아니기 때문입니다. 그 때. 예를 들어 다음 코드에서는 setTimeout() 및 setImmediate()의 실행 순서가 확실하지 않습니다.
setTimeout(() => { console.log('timeout') }, 0) setImmediate(() => { console.log('immediate') })
말이 되긴 하지만 데모 없이는 아직 완전히 이해가 안 돼요, 지금 불안해요!
const fs = require('fs')fs.readFile('test.txt', () => { console.log('readFile') setTimeout(() => { console.log('timeout') }, 0) setImmediate(() => { console.log('immediate') }) })
readFile immediate timeout
Node.js에서 마이크로태스크는 이벤트 루프의 다양한 단계 사이에서 실행됩니다. 즉, 단계가 실행된 후에 마이크로태스크가 실행됩니다. . 마이크로태스크 대기열의 작업입니다.
demo review
글 시작 부분에서 데모를 검토하세요. 스크립트(main( )) 실행, 2개의 타이머를 타이머 큐에 순서대로 넣고 main()이 실행되고 호출 스택이 유휴 상태가 되며 작업 큐가 실행을 시작합니다.
首先进入timers阶段,执行timer1的回调函数,打印timer1,并将promise1.then回调放入microtask队列,同样的步骤执行timer2,打印timer2;
至此,timer阶段执行结束,event loop进入下一个阶段之前,执行microtask队列的所有任务,依次打印promise1、promise2。
对比浏览器端的处理过程:
process.nextTick() VS setImmediate()
In essence, the names should be swapped. process.nextTick() fires more immediately than setImmediate()
来自官方文档有意思的一句话,从语义角度看,setImmediate() 应该比 process.nextTick() 先执行才对,而事实相反,命名是历史原因也很难再变。
process.nextTick() 会在各个事件阶段之间执行,一旦执行,要直到nextTick队列被清空,才会进入到下一个事件阶段,所以如果递归调用 process.nextTick(),会导致出现I/O starving(饥饿)的问题,比如下面例子的readFile已经完成,但它的回调一直无法执行:
const fs = require('fs')const starttime = Date.now()let endtime fs.readFile('text.txt', () => { endtime = Date.now() console.log('finish reading time: ', endtime - starttime)})let index = 0function handler () { if (index++ >= 1000) return console.log(`nextTick ${index}`) process.nextTick(handler) // console.log(`setImmediate ${index}`) // setImmediate(handler)}handler()
process.nextTick()的运行结果:
nextTick 1 nextTick 2 ...... nextTick 999 nextTick 1000 finish reading time: 170
替换成setImmediate(),运行结果:
setImmediate 1 setImmediate 2 finish reading time: 80 ...... setImmediate 999 setImmediate 1000
这是因为嵌套调用的 setImmediate() 回调,被排到了下一次event loop才执行,所以不会出现阻塞。
总结
1、Node.js 的事件循环分为6个阶段
2、浏览器和Node 环境下,microtask 任务队列的执行时机不同
Node.js中,microtask 在事件循环的各个阶段之间执行
浏览器端,microtask 在事件循环的 macrotask 执行完之后执行
3、递归的调用process.nextTick()会导致I/O starving,官方推荐使用setImmediate()
위 내용은 Node.js의 이벤트 루프 메커니즘 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!