Node.js   发布时间:2022-04-24  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了理解 Node.js 的 Event loop大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

问题

察如下代码,脑回路中运行并输出结果:

console.log("1");

setTimeout(function setTimeout1() {
  console.log("2");
  process.nextTick(function nextTick1() {
    console.log("3");
  });
  new Promise(function promise1(resolve) {
    console.log("4");
    resolve();
  }).then(function promisethen1() {
    console.log("5");
  });
  setImmediate(function immediate1() {
    console.log("immediate");
  });
});

process.nextTick(function nextTick2() {
  console.log("6");
});

function bar() {
  console.log("bar");
}

async function foo() {
  console.log("async start");
  await bar();
  console.log("async end");
}

foo();

new Promise(function promise2(resolve) {
  console.log("7");
  resolve();
}).then(function promisethen2() {
  console.log("8");
});

setTimeout(function setTimeout2() {
  console.log("9");
  
  new Promise(function promise3(resolve) {
    console.log("11");
    resolve();
  }).then(function promisethen3() {
    console.log("12");
  });

  process.nextTick(function nextTick3() {
    console.log("10");
  });
});

JS 事件循环

JS 是单线程,朴素地讲,同时只能完成一件事件。如果有耗时的任务,那后续的所有任务都要等待其完成才能执行。

为了避免这种阻塞,引入了事件循环。即,将代码的执行分成一个个很小的阶段(一次循环),每个阶段重复相应的事情,直到所有任务都完成。

一个阶段包含以下部分:

  • Timers:到期的定时器任务,setTimeout@H_851_301@,seTinterval@H_851_301@ 等注册的任务。
  • IO CallBACks:IO 操作,比如网络请求,文件读写。
  • IO Polling:IO 任务的注册
  • Set Immediate:通过 setImmediate@H_851_301@ 注册的任务
  • Close:close@H_851_301@ 事件的回调,比如 TCP 的断开。

理解 Node.js 的 Event loop

Ticks and Phases of the Node.js Event Loop 图片来自 Daniel Khan 的 Medium 博客,见文末

同步代码及上面每个环节结束时都会清空一遍微任务队列,记住这点很重要!

代码执行流程

执行的流程是,

  • 代码顺序执行。
  • 遇到异步任务,将任务压入待执行队列后继续往下。
  • 完成同步代码后,检查是否有微任务(通过 Promise@H_851_301@,process.nextTick@H_851_301@,async/await@H_851_301@ 等注册),如果有,则清空。
  • 清空微任务队列后,从待执行队列中取出最先压入的任务顺序执行,重复步骤一。

另,

  • async/await@H_851_301@ 本质上是 Promise@H_851_301@,所以其表现会和 Promise 一致。
  • process.nextTick@H_851_301@ 注册的回调优先级高于定时器。
  • setImmediate@H_851_301@ 可看成 Node 版本的 setTimeout@H_851_301@,所以可与后者同等对待。

示例代码分析

Round 1

  • 首先遇到同步代码 console.log(1)@H_851_301@,立即执行输出 1@H_851_301@
  • 接下来是一个 setTimeout@H_851_301@ 定时器,将其回调压入待执行队列 [setTimeout1]@H_851_301@
  • 遇到 process.nextTick@H_851_301@,将其回调 nextTick2@H_851_301@ 压入微任务队列 [nextTick2]@H_851_301@
  • 然后是 async 函数 foo@H_851_301@ 的调用,立即执行并输出 async start@H_851_301@
  • 然后是 await@H_851_301@ 语句,这所在的地方会创建并返回 Promise,所以这里会执行其后面的表达式,也就是 bar()@H_851_301@ 函数调用
  • 执行 bar@H_851_301@ 函数输出 bar@H_851_301@
  • 在执行了 await @H_851_301@ 后面的语句后,它所代表的 Promise 就创建完成了,foo@H_851_301@ 函数体后续的代码相当于 promise 的 then@H_851_301@,放入微任务队列 [nextTick2,rest_of_foo]@H_851_301@
  • 继续往下遇到 new Promise@H_851_301@,执行 Promise 的创建输出 7@H_851_301@,将它的 then@H_851_301@ 回调压入微任务队列 [nextTick2,rest_of_foo,promisethen2]@H_851_301@
  • 遇到另一个 setTimeout@H_851_301@,回调压入待执行队列 [setTimeout1,setTimeout2]@H_851_301@
  • 至此,代码执行完了一轮。此时的输出应该是 1,async start,bar,7@H_851_301@

Round 2

  • 查看微任务队列,并清空。所以依次执行 [nextTick2,promisethen2]@H_851_301@,输出 6,async end,8@H_851_301@。

Round 3

  • 查看待执行队列 [setTimeout1,setTimeout2]@H_851_301@,先执行 setTimout1@H_851_301@
  • 遇到 console.log(2)@H_851_301@ 输出2
  • 遇到 process.nextTick@H_851_301@ 将 nextTick1@H_851_301@ 压入微任务队列 [nextTick1]@H_851_301@
  • 遇到 new Promise@H_851_301@ 立即执行 输出 4@H_851_301@,执行 resolve()@H_851_301@ 后将 promisethen1@H_851_301@ 压入微任务队列 [nextTick1,promisethen1]@H_851_301@
  • 遇到 setImmediate@H_851_301@ 将回调压入待执行队列 [setTimeout2,immediate1]@H_851_301@
  • 此时 setTimeout1@H_851_301@ 执行完毕,此时的输出应该为 2,4@H_851_301@

Round 4

  • 检查微任务队列 [nextTick1,promisethen1]@H_851_301@ 依次执行并输出 3,5@H_851_301@

Round 5

  • 检查待执行队列 [setTimeout2,immediate1]@H_851_301@,执行 setTimeout2@H_851_301@
  • 遇到 console@H_851_301@输出 9@H_851_301@
  • 遇到 new Promise@H_851_301@ 执行并输出 11@H_851_301@,将 promisethen3@H_851_301@ 压入微任务队列 [promisethen3]@H_851_301@
  • 遇到 process.nextTick@H_851_301@ 将 nextTick3@H_851_301@ 压入微执行队列。注意,因为 process.nextTick@H_851_301@ 的优化级高于 Promise,所以压入后的结果是: [nextTick3,promisethen3]@H_851_301@
  • 此时 setTimeout2@H_851_301@ 执行完毕,输出9,11@H_851_301@

Round 6

  • 检查微任务队列 [nextTick3,promisethen3]@H_851_301@ 执行并输出 10,12@H_851_301@

Round 7

  • 检查待执行队列 [immediate1]@H_851_301@,执行并输出 immediate@H_851_301@

至此,走完了所有代码

结果

以下是文章开头的结果:

1
async start
bar
7
6
async end
8
2
4
3
5
9
11
10
12
immediate

大佬总结

以上是大佬教程为你收集整理的理解 Node.js 的 Event loop全部内容,希望文章能够帮你解决理解 Node.js 的 Event loop所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。