nodejs中架构如下图所示,通过v8引擎来执行js代码,通过中间层 libuv 来读写文件系统、网络等做一些操作。
 
 
nodejs中提供阻塞和非阻塞的调用方式,比如fs模块中读取文件,可以根据需要使用 readFile(异步) 或者 readFileSync(同步)。
 
如果使用同步的编程方式,那么后续代码的执行需要等到此次执行结束,程序的运行会被“阻塞”。也可以使用异步的编程方式,后续代码的执行无需等待此次的执行结束,不会“阻塞”程序的运行,但它同样也会存在一个问题,那就是非阻塞式调用需要不断的轮询获取异步调用的执行结果。
 
libuv中的io操作就是使用的”非阻塞式调用“,但它如果不断轮询获取结果这个过程对系统有一定的性能影响,为了将这个影响降低,libuv中将不断轮询的这个过程放到“线程池”当中,当轮询到结果时,将对应的回调和获取的结果放置到 event loop(事件循环)的某一个队列中,事件循环再来进行下一步的操作,通过javascript来执行回调。
 
 
nodejs中的事件循环要比javascript的事件循环(设置一个链接)更为复杂一些,一次循环分为以下几个阶段
* 定时器(timer): 在这个阶段执行 setTimeout、setInterval的回调函数
* 待定回调(pending callbacks):某些系统操作(如 TCP 错误类型)执行回调
* idle, prepare:仅系统内部使用。
* 轮询(poll):检索新的 I/O 事件; 执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外)
* 检测(check):setImmediate() 回调函数在这里执行。
* 关闭的回调函数(close callbacks):一些关闭的回调函数,如:socket.on('close', ...)。
 
nodejs中也和javascript中一样存在着微任务(micro-task)、宏任务(macro-task),两个任务中执行的内容也有一部分的相似性
微任务:promise的then函数的回调、queueMicrotask、process.nextTick
宏任务: setTimeout、setInterval、io事件、setImmediate、close事件

执行顺序也和javascript中一致,先执行主线程的任务,然后接着执微任务,微任务执行完成再执行宏任务,具体的执行顺序如下。

微任务队列
next tick queue:process.nextTick
other tick queue:promise的then函数、queueMicrotask 宏任务队列
timer queue: setTimeout、setInterval
poll queue: io事件
check queue: setImmediate
close queue: close事件

了解完nodejs中事件循环的执行顺序之后,一起来看看下面这道面试题

async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
} async function async2() {
console.log('async2')
} console.log('script start') setTimeout(function () {
console.log('setTimeout0')
}, 0) setTimeout(function () {
console.log('setTimeout2')
}, 300) setImmediate(() => console.log('setImmediate')); process.nextTick(() => console.log('nextTick1')); async1(); process.nextTick(() => console.log('nextTick2')); new Promise(function (resolve) {
console.log('promise1')
resolve();
console.log('promise2')
}).then(function () {
console.log('promise3')
}) console.log('script end')

首先声明了 async1 和 async2函数,只有调用才会被放入调用栈中,所以此时不会执行,往下执行输出 "script start"。

 
继续向下执行,将 "setTimeout0" 放入 宏任务的 timer queue 中,继续执行,"setTimeout2" 需要延迟300ms执行,不放入 timer queue中,接着把 "setImmediate" 放入 check queue 中,把 "nextTick1" 放入 next tick queue 中。
 
此时执行函数 async1,输出 "async1 start",在函数 async1 中执行 async2,输出 "async2",把 "async1 end" 放入 other queue当中,此时函数 async1执行完成,继续向下执行,将"nextTick2"放入next tick queue中,排在"nextTick1"后面,继续输出 "promise1",将"promise3"放在other queue中,排在"async1 end"后面,输出"promise2",最后输出"script end",此时主线程的内容都执行完成。
 
来到微任务队列,微任务队列中先执行 next tick queue 中的内容,依次输出 "nectTick1"、"nectTick2",再执行 other queue 中的内容,依次输出"async1 end"、"promise3"。
 
最后执行宏任务队列中的任务,先执行 timer queue,输出 "setTimeout0",这里没有io事件(poll),往下执行check quue,输出 setImmediate,没有关闭回调函数阶段( close callbasks),一次事件循环结束,来到第二次、第三次事件循环,此时300ms后,"setTimeout2" 被加入到宏任务队列中的 timer queue,事件循环中没有其他的队列,直接输出 "setTimeout2",事件循环结束。
 
简单图示如下
 
再来看另一道面试题
setTimeout(() => {
console.log("setTimeout");
}, 0); setImmediate(() => {
console.log("setImmediate");
});

按照宏任务队列中各任务的执行顺序,setTimeout属于timer queue,setImmediate属于check queue,按理说会先输出setTimeout,但实际情况会是什么样的呢,我们看一下以下输出情况

 
为什么会这样的情况呢,有时候是setTimeout先输出,有时候是setImmediate先输出,原因在于,setTimeout的回调函数虽然是延迟0毫秒执行,但是 setTimeout的准备时间要长于 event loop 的启动时间,当event loop 开始第一次循环的时候,setTimeout 还没有被放入 timer queue 之中,所以 event loop 先执行 check queue 中的 setImmediate,等待第二次循环的时候,timer queue 中才有setTimeout,此时就会出现 setImmediate 先输出的情况。 
 
nodejs中的事件循环机制有一部分和javascript中事件循环是一致的,如果对javascript事件循环机制还不太熟悉,可以看看这一篇文章,javascript事件循环机制及面试题详解
 

nodejs中事件循环机制与面试题详解的更多相关文章

  1. 安卓中的消息循环机制Handler及Looper详解

    我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handl ...

  2. nodejs的事件循环1

    JavaScript的学习零散而庞杂,因此很多时候我们学到了一些东西,但是却没办法感受到自己的进步,甚至过了不久,就把学到的东西给忘了.为了解决自己的这个困扰,在学习的过程中,我一直试图在寻找一条核心 ...

  3. JS基础-事件循环机制

    从一道题浅说 JavaScript 的事件循环 原文链接: https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7 ...

  4. Netty源码解析 -- 事件循环机制实现原理

    本文主要分享Netty中事件循环机制的实现. 源码分析基于Netty 4.1 EventLoop 前面分享服务端和客户端启动过程的文章中说过,Netty通过事件循环机制(EventLoop)处理IO事 ...

  5. JavaScript中的事件循环机制跟函数柯里化

    一.事件循环机制的理解 test();//按秒输出5个5 function test() { for (var i = 0; i < 5; i++) { setTimeout(() => ...

  6. 浏览器中的JavaScript事件循环机制

    浏览器的事件循环机制是HTML中定义的规范. JavaScript有一个主线程和调用栈,所有的任务都会被放到调用栈等待主线程执行. JS调用栈 是一种先进后出的数据结构.当函数被调用时,会被添加到栈中 ...

  7. 浏览器中 JS 的事件循环机制

    目录 事件循环机制 宏任务与微任务 实例分析 参考 1.事件循环机制 浏览器执行JS代码大致可以分为三个步骤,而这三个步骤的往复构成了JS的事件循环机制(如图). 第一步:主线程(JS引擎线程)中执行 ...

  8. js事件循环机制辨析

     对于新接触js语言的人来说,最令人困惑的大概就是事件循环机制了.最开始这也困惑了我好久,花了我几个月时间通过书本,打代码,查阅资料不停地渐进地理解他.接下来我想要和大家分享一下,虽然可能有些许错误的 ...

  9. JS:事件循环机制、调用栈以及任务队列

    点击查看原文 写在前面 js里的事件循环机制十分有趣.从很多面试题也可以看出来,考察简单的setTimeout也就是考察这个机制的. 在之前,我只是简单地认为由于函数执行很快,setTimeout执行 ...

  10. javaScript 事件循环机制

    JavaScript是单线程的编程语言,只能同一时间内做一件事.但是在遇到异步事件的时候,js线程并没有阻塞,还会继续执行,这就是因为JS有事件循环机制. 事件循环流程总结 主线程开始执行一段代码, ...

随机推荐

  1. 云原生时代崛起的编程语言Go常用标准库实战

    @ 目录 基础标准库 简述 字符串-string 底层结构 函数 长度 格式化输出 模版-template text/template html/template 正则表达式-regexp 编码-en ...

  2. PCI-5565系列反射内存卡 反射内存交换机

    主要性能:1路发射,一路接收光纤高速网络2.125GHz.最大256个节点.在板128MByte SDRAM.光纤通讯协议不占用CPU资源.动态包长,每个包4 到 64 个字节.33MHz PCI 3 ...

  3. vue+vant项目中 rem适配配置

    vant rem适配,需要安装两个插件 postcss-pxtorem 是一款 postcss 插件,用于将单位转化为 rem lib-flexible 用于设置 rem 基准值 postcss-px ...

  4. 2021-02-07:给定两棵二叉树的头节点head1和head2,如何判断head1中是否有某个子树的结构和head2完全一样?

    福哥答案2021-02-07: 对head1和head2序列化为str1和str2.然后用kmp算法去判断str2是否是str1的子串.如果是,head2是子树:如果不是,head2不是子树. 代码用 ...

  5. 2022-05-23:给定一个数组arr,你可以随意挑选其中的数字, 但是你挑选的数中,任何两个数a和b,必须Math.abs(a - b) > 1。 返回你最多能挑选几个数。 来自美团。

    2022-05-23:给定一个数组arr,你可以随意挑选其中的数字, 但是你挑选的数中,任何两个数a和b,必须Math.abs(a - b) > 1. 返回你最多能挑选几个数. 来自美团. 答案 ...

  6. Linux 大页内存 Huge Pages 虚拟内存

    Linux为什么要有大页内存?为什么DPDK要求必须要设置大页内存?这都是由系统架构决定的,系统架构发展到现在,又是在原来的基础上一点点演变的.一开始为了解决一个问题,大家设计了一个很好的方案,随着事 ...

  7. 旧版Vue配置API_ROOT,开发、生产地址切换

    1 目录 config/dev.env.js1 'use strict' 2 const merge = require('webpack-merge') 3 const prodEnv = requ ...

  8. [MAUI]写一个跨平台富文本编辑器

    @ 目录 原理 创建编辑器 定义 实现复合样式 选择范围 字号 字体颜色与背景色 字体下划线 字体加粗与斜体 序列化和反序列化 跨平台实现 集成至编辑器 创建控件 使用控件 最终效果 已知问题 项目地 ...

  9. auto main()-> int的含义是什么?

    42 https://stackoverflow.com/questions/21085446/what-is-the-meaning-of-auto-main-int/21085530   C++1 ...

  10. 【原创】xenomai内核解析-xenomai实时线程创建流程

    版权声明:本文为本文为博主原创文章,未经同意,禁止转载.如有错误,欢迎指正,博客地址:https://www.cnblogs.com/wsg1100/ 目录 问题概述 1 libCobalt中调用非实 ...