>  기사  >  웹 프론트엔드  >  nodejs 환경의 JavaScript 실행 메커니즘 및 이벤트 루프

nodejs 환경의 JavaScript 실행 메커니즘 및 이벤트 루프

不言
不言앞으로
2019-04-02 10:59:592491검색

이 글은 JavaScript 고차 함수의 사용법을 소개합니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

1. 설명

nodejs는 단일 스레드에서 실행되며 이벤트 기반 비차단 IO 프로그래밍 모델을 기반으로 합니다. 이를 통해 비동기 작업 결과가 반환될 때까지 기다리지 않고 코드를 계속 실행할 수 있습니다. 비동기 이벤트가 트리거되면 메인 스레드에 알림이 전달되고, 메인 스레드는 해당 이벤트의 콜백을 실행합니다.

이 글은 노드에서 JavaScript 코드의 실행 과정을 설명합니다. 다음은 출력 결과를 알고 있다면 이 글을 읽을 필요가 없습니다. 이해하는 데 도움이 될 수 있습니다.

console.log(1)
setTimeout(function () {
  new Promise(function (resolve) {
    console.log(2)
    resolve()
  })
  .then(() => { console.log(3) })
})
setTimeout(function () {
  console.log(4)
})

복잡함:

setTimeout(() => {
  console.log('1')
  new Promise((resolve) => { console.log('2'); resolve(); })
  .then(() => { console.log('3') })
  new Promise((resolve)=> { console.log('4'); resolve()})
  .then(() => { console.log('5') })
  setTimeout(() => { 
    console.log('6')
    setTimeout(() => {
      console.log('7')
      new Promise((resolve) => { console.log('8'); resolve() })
      .then( () => {  console.log('9') })
      new Promise((resolve) => { console.log('10'); resolve() })
      .then(() => {  console.log('11') })
    })
    setTimeout(() => { console.log('12') }, 0)
  })
  setTimeout(() => { console.log('13') }, 0)
})
setTimeout(() => { console.log('14') }, 0)
new Promise((resolve) => { console.log('15'); resolve() })
.then( ()=> { console.log('16') })
new Promise((resolve) => { console.log('17'); resolve() })
.then(() => { console.log('18') })

2. nodejs

node.js 시작 프로세스는 다음 단계로 나눌 수 있습니다.

  1. platformInit 메서드를 호출하여 실행을 초기화합니다. nodejs의 환경.
  2. nodejs에서 성능 통계를 수행하려면performance_node_start 메소드를 호출하세요.
  3. openssl 설정 판단.
  4. libuv 스레드 풀을 초기화하려면 v8_platform.Initialize를 호출하세요.
  5. V8::Initialize를 호출하여 V8 환경을 초기화하세요.
  6. nodejs 실행 인스턴스를 만듭니다.
  7. 이전 단계에서 생성한 인스턴스를 시작합니다.
  8. js 파일 실행을 시작합니다. 동기화 코드가 실행된 후 이벤트 루프에 들어갑니다.
  9. 청취할 이벤트가 없으면 nodejs 인스턴스가 소멸되고 프로그램 실행이 완료됩니다.

nodejs 환경의 JavaScript 실행 메커니즘 및 이벤트 루프

3. nodejs 이벤트 루프에 대한 자세한 설명

Nodejs는 메시지 루프를 6단계(공식적으로는 Phase라고 함)로 세분화하며, 각 단계는 필요한 단계 콜백 기능을 저장하는 대기열과 같은 구조를 갖습니다.

Nodejs 특정 단계에서 너무 많은 작업이 후속 단계에서 기아를 유발하는 것을 방지하기 위해 메시지 루프의 각 반복마다 최대 실행 콜백 수가 초과되면 현재 단계가 처리됩니다. 이 규칙은 메시지 루프의 모든 단계에 적용됩니다.

3.1 타이머 단계

이것은 for 루프가 있는 메시지 루프의 첫 번째 단계입니다. 모든 setTimeoutsetInterval 콜백을 처리합니다. for 循环处理所有 setTimeoutsetInterval 的回调.

这些回调被保存在一个最小堆(min heap) 中. 这样引擎只需要每次判断头元素, 如果符合条件就拿出来执行, 直到遇到一个不符合条件或者队列空了, 才结束 Timer Phase.

Timer 阶段中判断某个回调是否符合条件的方法也很简单. 消息循环每次进入 Timer 的时候都会保存一下当时的系统时间,然后只要看上述最小堆中的回调函数设置的启动时间是否超过进入 Timer 时保存的时间, 如果超过就拿出来执行.

3.2 Pending I/O Callback 阶段

执行除了close callbackssetTimeout()setInterval()setImmediate()回调之外几乎所有回调,比如说TCP连接发生错误fs.read, socket 等 IO 操作的回调函数, 同时也包括各种 error 的回调.

3.3 Idle, Prepare 阶段

系统内部的一些调用。

3.4 Poll 阶段,重要阶段

这是整个消息循环中最重要的一个 阶段, 作用是等待异步请求和数据,因为它支撑了整个消息循环机制.

poll阶段有两个主要的功能:一是执行下限时间已经达到的timers的回调,一是处理poll队列里的事件。
注:Node的很多API都是基于事件订阅完成的,比如fs.readFile,这些回调应该都在poll阶段完成。

当事件循环进入poll阶段:

  • poll队列不为空的时候,事件循环肯定是先遍历队列并同步执行回调,直到队列清空或执行回调数达到系统上限。
  • poll队列为空的时候,这里有两种情况。

    • 如果代码已经被setImmediate()设定了回调,那么事件循环直接结束poll阶段进入check阶段来执行check队列里的回调。
    • 如果代码没有被设定setImmediate()

      이러한 콜백은 이러한 방식으로 헤더 요소가 한 번만 판단됩니다. 조건을 만족하면 꺼내어 실행됩니다. 타이머 단계는 조건을 충족하지 않는 조건을 만나거나 대기열이 비어 있을 때까지 종료되지 않습니다. 타이머 단계에서는 특정 콜백이 조건을 충족하는지 판단하는 방법입니다. 메시지 루프도 매우 간단합니다. Timer를 입력할 때마다 해당 시간의 시스템 시간이 저장되며, 위의 최소 힙에 콜백 함수로 설정한 시작 시간이 Timer 입력 시 저장된 시간을 초과하는지 확인하면 됩니다. 초과하면 꺼내서 실행하세요.🎜🎜3.2 보류 중인 I/O 콜백 단계🎜🎜 콜백 닫기, setTimeout(), setInterval(을 제외한 콜백 실행 ), setImmediate() 오류 콜백을 제외한 거의 모든 콜백.🎜🎜3.3 유휴, 준비 단계🎜🎜시스템 내 일부 호출. 🎜🎜3.4 폴 단계, 중요한 단계🎜🎜전체 메시지 주기 메커니즘을 지원하므로 비동기 요청과 데이터를 기다리는 역할이 전체 메시지 주기에서 가장 중요합니다.🎜🎜폴 단계에는 두 가지 주요 단계가 있습니다. 기능: 하나는 하한 시간에 도달한 타이머의 콜백을 실행하는 것이고, 다른 하나는 폴 큐에 있는 이벤트를 처리하는 것입니다.
      🎜참고:🎜Node의 많은 API는 fs.readFile과 같은 이벤트 구독을 기반으로 합니다. 이러한 콜백은 poll 단계에서 완료되어야 합니다. 🎜🎜이벤트 루프가 폴 단계에 들어갈 때: 🎜
        🎜poll 대기열이 비어 있지 않으면 이벤트 루프는 먼저 대기열을 순회하고 대기열이 지워지거나 종료될 때까지 동기식으로 콜백을 실행해야 합니다. 실행된 콜백 수가 시스템 상한에 도달했습니다. 🎜🎜🎜poll 대기열이 비어 있으면 두 가지 상황이 있습니다. 🎜
          🎜setImmediate()에 의한 콜백으로 코드가 설정된 경우 이벤트 루프는 poll 단계를 직접 종료하고 check 실행 단계 <code>check큐의 콜백. 🎜🎜🎜코드가 setImmediate() 설정되지 않은 경우 콜백 설정: 🎜
          • 타이머가 설정된 경우 이벤트 루프는 이때 타이머를 확인합니다. 하나 이상의 타이머의 하한 시간에 도달하면 이벤트 루프가 타이머 단계를 순환하고 효과적인 콜백 대기열을 실행합니다. 타이머의.
          • 타이머가 설정되지 않은 경우 현재 이벤트 루프는 blocking이며, 폴 단계 중에 이벤트 콜백이 폴 큐에 추가될 때까지 기다립니다.

Poll 단계에서 js 레이어 코드에 의해 등록된 이벤트 콜백이 반환되지 않으면 이벤트 루프가 Poll 단계에서 일시적으로 차단됩니다. 차단 해제 조건은 다음과 같습니다.

  1. 실행될 때. 폴링 단계에서는 폴링 단계의 최대 차단 시간인 시간 초과 시간 초과를 전달합니다.
  2. 타임아웃 시간이 지나지 않은 상태에서 이벤트가 반환되면 해당 이벤트에 등록된 콜백 함수가 실행됩니다. 제한 시간 제한 시간이 만료되면 폴링 단계가 종료되고 다음 단계가 실행됩니다.

이 시간 초과에 대한 적절한 설정은 무엇입니까? 대답은 타이머 단계에서 실행될 가장 최근 콜백의 시작 시간과 현재의 차이입니다. 왜냐하면 이 차이가 대기 중인 콜백이 없기 때문입니다. 폴 단계 후에 실행되므로 델타 기간 동안 가장 많이 기다립니다. 해당 기간 동안 이벤트가 메시지 루프를 깨우면 해당 기간 동안 아무 일도 일어나지 않으면 다음 단계의 작업을 계속합니다. 메시지 루프는 다음 반복의 타이머 단계를 허용하기 위해 후속 단계로 들어가야 합니다. 또한 실행될 수도 있습니다.
Nodejs는 IO 이벤트와 커널 비동기 이벤트 도착을 기다리면서 전체 메시지 루프를 폴 단계로 구동합니다.

3.5 확인 단계

이 단계에서는 setImmediate 콜백 함수만 처리합니다.

여기에 setImmediate를 처리하는 특별한 단계가 있는 걸까요? 간단히 말해서, 이는 Poll 단계에서 일부 콜백을 설정하고 Poll 단계 이후에 실행될 수 있기 때문입니다. . 따라서 이 Check 단계는 Poll 단계 뒤에 추가됩니다.


3.6 Close Callbacks 단계

socket.on('close', ...)와 같은 일부 닫기 유형 콜백을 특별히 처리합니다. . 리소스 정리에 사용됩니다.

4. Nodejs는 JS 코드 프로세스 및 이벤트 루프 프로세스를 실행합니다.

1. 노드 초기화socket.on('close', ...). 用于资源清理.

4. nodejs执行JS代码过程及事件循环过程

1、node初始化

初始化node环境

执行输入的代码

执行process.nextTick回调

执行微任务(microtasks)

2、进入事件循环

2.1、进入Timer阶段

  • 检查Timer队列是否有到期的Timer的回调,如果有,将到期的所有Timer回调按照TimerId升序执行
  • 检查是否有process.nextTick任务,如果有,全部执行
  • 检查是否有微任务(promise),如果有,全部执行
  • 退出该阶段

2.2、进入Pending I/O Callback阶段

  • 检查是否有Pending I/O Callback的回调,如果有,执行回调。如果没有退出该阶段
  • 检查是否有process.nextTick任务,如果有,全部执行
  • 检查是否有微任务(promise),如果有,全部执行
  • 退出该阶段

2.3、进入idle,prepare阶段

这个阶段与JavaScript关系不大,略过

2.4、进入Poll阶段

首先检查是否存在尚未完成的回调,如果存在,分如下两种情况:

第一种情况:有可执行的回调

执行所有可用回调(包含到期的定时器还有一些IO事件等)

检查是否有process.nextTick任务,如果有,全部执行

检查是否有微任务(promise),如果有,全部执行

退出该阶段

第二种情况:没有可执行的回调

检查是否有immediate回调,如果有,退出Poll阶段。如果没有,阻塞在此阶段,等待新的事件通知

如果不存在尚未完成的回调,退出Poll阶段

2.5、进入check阶段

如果有immediate回调,则执行所有immediate回调

检查是否有process.nextTick任务,如果有,全部执行

检查是否有微任务(promise),如果有,全部执行

退出该阶段

2.6、进入closing阶段

如果有immediate回调,则执行所有immediate回调

检查是否有process.nextTick任务,如果有,全部执行

检查是否有微任务(promise),如果有,全部执行

退出该阶段

3、检查是否有活跃的handles(定时器、IO等事件句柄)

노드 환경 초기화


입력된 코드 실행

프로세스 실행 .nextTick 콜백

마이크로태스크(microtasks) 실행2. 이벤트 루프 진입 2.1 Timer 단계 진입

Timer' 확인 > 대기열에는 만료된 Timer에 대한 콜백이 있습니다. 그렇다면 만료된 모든 Timer는 code>Timer 콜백이 TimerId의 오름차순으로 실행됩니다. code><p><br><code>process.nextTick 작업이 있는지 확인하고, 있으면 모두 실행하세요

마이크로 작업(promise)이 있는지 확인하고, 그렇다면 모두 실행하세요

🎜이 단계를 종료하세요🎜🎜 🎜2.2. Pending I/O 콜백 단계로 들어갑니다🎜🎜🎜 Pending I/O 콜백 콜백이 있는지 확인하세요. 있으면 콜백을 실행합니다. 🎜이 단계를 종료하지 않으면🎜🎜🎜process.nextTick 작업이 있는지 확인하고, 있으면 모두 실행하세요.🎜🎜마이크로 작업(프라미스)이 있는지 확인하고, 있으면 실행하세요. all🎜🎜이 단계 종료 🎜🎜🎜2.3 idle, prepare 단계 진입 🎜🎜이 단계는 JavaScript와 관련이 없으므로 건너뛰기 🎜🎜2.4. > stage 🎜🎜먼저 존재 여부를 확인합니다. 존재하는 경우 완료된 콜백은 다음 두 가지 상황으로 나뉩니다. 🎜🎜첫 번째 상황: 실행 가능한 콜백이 있습니다. 🎜🎜사용 가능한 모든 콜백을 실행합니다(만료된 타이머 및 일부 IO 포함). 이벤트 등) 🎜🎜process.nextTick 작업이 있는지 확인하고, 있으면 모두 실행🎜🎜마이크로 작업(프라미스)이 있는지 확인하고, 있으면 모두 실행🎜 🎜🎜이 단계 종료🎜🎜두 번째 경우: 실행 가능한 콜백이 없습니다🎜🎜immediate 콜백이 있는지 확인하고, 그렇다면 Poll 단계를 종료합니다. 그렇지 않은 경우 이 단계를 차단하고 새 이벤트 알림을 기다리세요🎜🎜🎜완료되지 않은 콜백이 없으면 Poll 단계를 종료합니다🎜🎜🎜2.5, check 단계로 들어갑니다🎜🎜즉시 콜백이 있으면 콜백한 다음 즉시 콜백을 모두 실행하세요🎜🎜process.nextTick 작업이 있는지 확인하고, 있으면 모두 실행하세요🎜🎜🎜마이크로 작업(약속)이 있는지 확인하고, 그렇다면 모두 실행하세요🎜 🎜이 단계 종료🎜🎜 2.6. 종료 단계 진입🎜🎜즉시 콜백이 있으면 모든 즉시 콜백을 실행🎜🎜process.nextTick 작업이 있는지 확인 , 그렇다면 모두 실행🎜🎜마이크로 태스크(프라미스)가 있는지 확인하고, 그렇다면 모두 실행하세요🎜🎜이 단계에서 나가세요🎜🎜3. 활성 핸들(타이머, IO 및 기타 이벤트 핸들)이 있는지 확인하세요. )🎜🎜있는 경우 이벤트 루프 1회를 계속합니다🎜🎜🎜그렇지 않은 경우 이벤트 루프를 종료하고 프로그램을 종료합니다🎜🎜🎜🎜참고: 🎜🎜🎜🎜이벤트 루프의 모든 하위 단계는 종료하기 전에 다음 프로세스를 순서대로 실행하십시오. 🎜🎜process.nextTick 콜백이 있는지 확인하십시오. 콜백이 있으면 모두 실행됩니다. 🎜🎜🎜마이크로 태스크(프라미스)가 있는지 확인하고, 있다면 모두 실행하세요. 🎜

4.1 关于Promise和process.nextTick

事件循环队列先保证所有的process.nextTick回调,然后将所有的Promise回调追加在后面,最终在每个阶段结束的时候一次性拿出来执行。

此外,process.nextTickPromise回调的数量是受限制的,也就是说,如果一直往这个队列中加入回调,那么整个事件循环就会被卡住

nodejs 환경의 JavaScript 실행 메커니즘 및 이벤트 루프

4.2 关于setTimeout(…, 0) 和 setImmediate

这两个方法的回调到底谁快?

如下面的例子:

setImmediate(() => console.log(2))
setTimeout(() => console.log(1))

使用nodejs多次执行后,发现输出结果有时是1 2,有时是2 1

对于多次执行输出结果不同,需要了解事件循环的基础问题。

首先,Nodejs启动,初始化环境后加载我们的JS代码(index.js).发生了两件事(此时尚未进入消息循环环节):

setImmediate 向 Check 阶段 中添加了回调 console.log(2);

setTimeout 向 Timer 阶段 中添加了回调 console.log(1)

这时候, 要初始化阶段完毕, 要进入 Nodejs 消息循环了。

为什么会有两种输出呢? 接下来一步很关键:

当执行到 Timer 阶段 时, 会发生两种可能. 因为每一轮迭代刚刚进入 Timer 阶段 时会取系统时间保存起来, 以 ms(毫秒) 为最小单位.

如果 Timer 阶段 中回调预设的时间 > 消息循环所保存的时间, 则执行 Timer 阶段 中的该回调. 这种情况下先输出 1, 直到 Check 阶段 执行后,输出2.总的来说, 结果是 1 2.

如果运行比较快, Timer 阶段 中回调预设的时间可能刚好等于消息循环所保存的时间, 这种情况下, Timer 阶段 中的回调得不到执行, 则继续下一个 阶段. 直到 Check 阶段, 输出 2. 然后等下一轮迭代的 Timer 阶段, 这时的时间一定是满足 Timer 阶段 中回调预设的时间 > 消息循环所保存的时间 , 所以 console.log(1) 得到执行, 输出 1. 总的来说, 结果就是 2 1.

所以, 输出不稳定的原因就取决于进入 Timer 阶段 的时间是否和执行 setTimeout 的时间在 1ms 内. 如果把代码改成如下, 则一定会得到稳定的输出:

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

这是因为消息循环在 Pneding I/O Phase 才向 Timer 和 Check 队列插入回调. 这时按照消息循环的执行顺序, Check 一定在 Timer 之前执行。

从性能角度讲, setTimeout 的处理是在 Timer Phase, 其中 min heap 保存了 timer 的回调, 因此每执行一个回调的同时都会涉及到堆调整. 而 setImmediate 仅仅是清空一个队列. 效率自然会高很多.

再从执行时机上讲. setTimeout(..., 0) 和 setImmediate 完全属于两个阶段.

5. 一个实际例子演示

下面以一段代码来说明nodejs运行JavaScript的机制。

如下面一段代码:

setTimeout(() => {                                                // settimeout1
  console.log('1')
  new Promise((resolve) => { console.log('2'); resolve(); })      // Promise3
  .then(() => { console.log('3') })
  new Promise((resolve)=> { console.log('4'); resolve()})         // Promise4
  .then(() => { console.log('5') })
  setTimeout(() => {                                              // settimeout3
    console.log('6')
    setTimeout(() => {                                            // settimeout5
      console.log('7')
      new Promise((resolve) => { console.log('8'); resolve() })   // Promise5
      .then( () => {  console.log('9') })
      new Promise((resolve) => { console.log('10'); resolve() })  // Promise6
      .then(() => {  console.log('11') })
    })
    setTimeout(() => { console.log('12') }, 0)                    // settimeout6
  })
  setTimeout(() => { console.log('13') }, 0)                      // settimeout4
})
setTimeout(() => { console.log('14') }, 0)                        // settimeout2
new Promise((resolve) => { console.log('15'); resolve() })        // Promise1
.then( ()=> { console.log('16') })
new Promise((resolve) => { console.log('17'); resolve() })        // Promise2
.then(() => { console.log('18') })

上面代码执行过程:

node初始化

执行JavaScript代码

遇到setTimeout, 把回调函数放到Timer队列中,记为settimeout1

遇到setTimeout, 把回调函数放到Timer队列中,记为settimeout2

遇到Promise,执行,输出15,把回调函数放到微任务队列,记为Promise1

遇到Promise,执行,输出17,把回调函数放到微任务队列,记为Promise2

代码执行结束,此阶段输出结果:15 17

没有process.nextTick回调,略过

执行微任务

检查微任务队列是否有可执行回调,此时队列有2个回调:Promise1、Promise2

执行Promise1回调,输出16

执行Promise2回调,输出18

此阶段输出结果:16 18

进入第一次事件循环

进入Timer阶段

检查Timer队列是否有可执行的回调,此时队列有2个回调:settimeout1、settimeout2

执行settimeout1回调:

输出1、2、4

添加了2个微任务,记为Promise3、Promise4

添加了2个Timer任务,记为settimeout3、settimeout4

执行settimeout2回调,输出14

Timer队列任务执行完毕

没有process.nextTick回调,略过

检查微任务队列是否有可执行回调,此时队列有2个回调:Promise3、Promise4

按顺序执行2个微任务,输出3、5

此阶段输出结果:1 2 4 14 3 5

Pending I/O Callback阶段没有任务,略过

进入 Poll 阶段

检查是否存在尚未完成的回调,此时有2个回调:settimeout3、settimeout4

settimeout3 콜백 실행

출력 6

타이머 작업 2개 추가, settimeout5, settimeout6으로 기록됨

settimeout4 콜백 실행, 13 출력

process.nextTick 콜백이 없습니다. 건너뛰세요process.nextTick回调,略过

没有微任务,略过

此阶段输出结果:6 13

check、closing阶段没有任务,略过

检查是否还有活跃的handles(定时器、IO等事件句柄),有,继续下一轮事件循环

进入第二次事件循环

进入Timer阶段

检查Timer队列是否有可执行的回调,此时队列有2个回调:settimeout5、settimeout6

执行settimeout5回调:

输出7、 8、10

添加了2个微任务,记为Promise5、Promise6

执行settimeout6回调,输出12

没有process.nextTick回调,略过

检查微任务队列是否有可执行回调,此时队列有2个回调:Promise5、Promise6

按顺序执行2个微任务,输出9、11

此阶段输出结果:7 8 10 12 9 11

Pending I/O Callback、Poll、check、closing阶段没有任务,略过

检查是否还有活跃的handles(定时器、IO等事件句柄),没有了,结束事件循环,退出程序

程序执行结束,输出结果:15 17 16 18 1 2 4 14 3 5 6 13 7 8 10 12 9 11

마이크로 작업이 없습니다. 건너뛰세요

nodejs 환경의 JavaScript 실행 메커니즘 및 이벤트 루프

이 단계의 출력 결과: 6 13

확인 및 종료 단계에 작업이 없습니다. 건너뛰어

하여 아직 활성 상태인지 확인하세요. 핸들(타이밍)(장치, IO 등의 이벤트 핸들), 그렇다면 이벤트 루프의 다음 라운드로 진행🎜🎜🎜두 번째 이벤트 루프에 진입🎜🎜타이머 단계에 진입🎜 🎜Timer 대기열에 실행 가능한 콜백이 있는지 확인하세요. 현재 대기열에는 2개의 콜백이 있습니다: settimeout5, settimeout6🎜🎜🎜Execute settimeout5 콜백: 🎜🎜Output 7, 8, 10🎜🎜🎜2개의 마이크로태스크가 추가되어 Promise5, Promise6으로 기록됩니다. 🎜🎜🎜settimeout6 콜백 실행, 12 출력🎜🎜🎜process.nextTick 콜백 없음, 건너뛰기 🎜🎜🎜 마이크로태스크 대기열에 실행 가능한 콜백이 있는지 확인하세요. 이때 대기열에는 Promise5, Promise6🎜🎜🎜2개의 마이크로 태스크를 순차적으로 실행하고 9, 11🎜🎜🎜을 출력합니다. 이 단계의 출력 결과: 7 8 10 12 9 11🎜🎜🎜Pending I/O 콜백, 폴링, 확인, 종료 단계에는 작업이 없습니다. 건너뛰고🎜🎜🎜 활성 핸들(타이머, IO 및 기타 이벤트 핸들)이 있는지 확인하세요. 더 이상 없으면 이벤트 루프를 종료하세요. 그리고 프로그램을 종료하세요🎜🎜🎜프로그램 실행이 종료되고 출력 결과는 다음과 같습니다: 15 17 16 18 1 2 4 14 3 5 6 13 7 8 10 12 9 11🎜🎜🎜🎜🎜🎜【관련 권장사항: 🎜JavaScript 비디오 튜토리얼🎜】🎜🎜 🎜

위 내용은 nodejs 환경의 JavaScript 실행 메커니즘 및 이벤트 루프의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제