浅谈Node.js中的定时器_node.js
Node.js中定时器的实现
上一篇博文提到,在Node中timer并不是通过新开线程来实现的,而是直接在event loop中完成。下面通过几个JavaScript的定时器示例以及Node相关源码来分析在Node中,timer功能到底是怎么实现的。
JavaScript中定时器功能的特点
无论是Node还是浏览器中,都有setTimeout和setInterval这两个定时器函数,并且其工作特点基本相同,因此下面仅以Node为例进行分析。
我们知道,JavaScript中的定时器并不同于计算机底层的定时中断。中断到来时,当前执行代码会被打断,转去执行定时中断处理函数。而JavaScript的定时器到时,如果当前执行线程没有正在执行的代码,则执行相应的回调函数;如果当前有代码在执行中,JavaScript引擎既不会中断当前代码转去执行回调,也不会开新的线程执行回调,而是当前代码执行完毕之后才去处理。
console.time('A') setTimeout(function () { console.timeEnd('A'); }, 100); var i = 0; for (; i < 100000; i++) { }
执行上面的代码,可以看到最终输出的时间并不是100ms左右,而是数秒。这说明在循环完成之前,定时回调函数确实没有被执行,而是推迟到了循环结束。实际上在JavaScript代码执行中,所有的事件都无法得到处理,必须等到当前代码全部完成,才能去处理新的事件。这就是为什么在浏览器中运行耗时JavaScript代码时,浏览器会失去响应。为了应对这种情况,我们可以采取Yielding Processes的技巧,将耗时的代码分成小块(chunks),每处理完一块就执行一次setTimeout,约定在一小段时间后才处理下一块,而在这段空闲时间里,浏览器/Node可以去处理排队中的事件。
补充资料
在JavaScript 高级程序设计 第三版第22章高级技巧中对高级定时器以及Yielding Processes有较详细的讨论。
Node中的timer实现
libuv对uv_loop_t类型的初始化
上一篇博文提到Node会调用libuv的uv_run函数启动default_loop_ptr进行事件调度,default_loop_ptr指向一个uv_loop_t类型的变量default_loop_struct。Node启动时会调用uv_loop_init(&default_loop_struct)对其进行初始化,uv_loop_init函数节选如下:
int uv_loop_init(uv_loop_t* loop) { ... loop->time = 0; uv_update_time(loop); ... }
可以看到loop的time字段先被赋值为0,之后调用uv_update_time函数,这会将最新的计数时间赋给loop.time。
初始化完成之后,default_loop_struct.time就有了一个初始值,与时间有关的操作都会与此值进行比较从而确定是否调用相应回调函数。
libuv的事件调度核心
前面提到uv_run函数就是libuv库实现event loop的核心部分,下面是其流程图:
这里简述一下上面与定时器相关的逻辑:
更新当前loop的time字段,这个字段标志着当前loop概念下的“现在”;
检查loop是否alive,也就是说检查loop中是否还有需要处理的任务(handlers/requests),如果没有就不必循环了;
检查注册过的timer,如果某一个timer中指定的时间落后于当前时间了,说明该timer已到时,于是执行其对应的回调函数;
执行一次I/O polling(即阻塞住线程,等待I/O事件发生),如果在下一个timer到期时还没有任何I/O完成,则停止等待,执行下一个timer的回调。
如果发生了I/O事件,则执行对应的回调;由于执行回调的时间里可能又有timer到期了,这里要再次检查timer并执行回调。
(实际上(4.)这里比较复杂,不仅仅是一步操作,这样描述仅是为了不涉及其他细节,而专注于timer的实现。)
Node会一直调用uv_run直到loop不再alive。
Node中的timer_wrap与timers
Node中有一个TimerWrap类,被注册为Node内部的timer_wrap模块。
NODE_MODULE_CONTEXT_AWARE_BUILTIN(timer_wrap, node::TimerWrap::Initialize)
其中TimerWrap类基本上就是对uv_timer_t的一个直接封装,NODE_MODULE_CONTEXT_AWARE_BUILTIN是Node用于注册built-in模块的宏。
经过这一步操作,JavaScript就可以拿到这个模块进行操作了。src/lib/timers.js文件使用JavaScript的形式把timer_wrap的功能封装起来,并导出了exports.setTimeout, exports.setInterval, exports.setImmediate等函数。
Node启动与global初始化
上一篇提到Node启动时会载入执行环境LoadEnvironment(env),这个函数中非常重要的一步就是载入src/node.js并执行,src/node.js会载入指定的模块并初始化global和process。当然,setTimeout等函数也会被src/node.js绑定到global对象上。
以上所述就是本文的全部内容了,希望大家能够喜欢。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

基于无阻塞、事件驱动建立的Node服务,具有内存消耗低的优点,非常适合处理海量的网络请求。在海量请求的前提下,就需要考虑“内存控制”的相关问题了。 1. V8的垃圾回收机制与内存限制 Js由垃圾回收机

选择一个Node的Docker镜像看起来像是一件小事,但是镜像的大小和潜在漏洞可能会对你的CI/CD流程和安全造成重大的影响。那我们如何选择一个最好Node.js Docker镜像呢?

文件模块是对底层文件操作的封装,例如文件读写/打开关闭/删除添加等等 文件模块最大的特点就是所有的方法都提供的**同步**和**异步**两个版本,具有 sync 后缀的方法都是同步方法,没有的都是异

您可以在 iPhone 相机上设置多长时间的定时器?当您在 iPhone 的相机应用程序中访问定时器选项时,您将获得在两种模式之间进行选择的选项:3 秒 (3s)和10 秒 (10s)。当您手持 iPhone 时,您可以使用第一个选项从前置或后置摄像头快速自拍。第二个选项在场景中很有用,可以在远处将 iPhone 安装到三脚架上来点击合影或自拍。 如何在 iPhone 相机上设置定时器 虽然在 iPhone 相机上设置定时器是一个相当简单的过程,但具体操作方式因所使用的 iPhone 机型而异。

事件循环是 Node.js 的基本组成部分,通过确保主线程不被阻塞来实现异步编程,了解事件循环对构建高效应用程序至关重要。下面本篇文章就来带大家深入了解Node中的事件循环 ,希望对大家有所帮助!

node无法用npm命令是因为没有正确配置环境变量,其解决办法是:1、打开“系统属性”;2、找到“环境变量”->“系统变量”,然后编辑环境变量;3、找到nodejs所在的文件夹;4、点击“确定”即可。

最开始的时候 JS 只在浏览器端运行,对于 Unicode 编码的字符串容易处理,但是对于二进制和非 Unicode 编码的字符串处理困难。并且二进制是计算机最底层的数据格式,视频/音频/程序/网络包
