浅谈Node.js中的定时器


Posted in Javascript onJune 18, 2015

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的核心部分,下面是其流程图:

浅谈Node.js中的定时器

这里简述一下上面与定时器相关的逻辑:

更新当前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对象上。

以上所述就是本文的全部内容了,希望大家能够喜欢。

Javascript 相关文章推荐
在线编辑器的实现原理(兼容IE和FireFox)
Mar 09 Javascript
javascript入门·对象属性方法大总结
Oct 01 Javascript
基于jquery实现状态限定编辑的代码
Feb 11 Javascript
JS实现可直接显示网页代码运行效果的HTML代码预览功能实例
Aug 06 Javascript
js阻止浏览器默认行为触发的通用方法(推荐)
May 15 Javascript
原生js编写2048小游戏
Mar 17 Javascript
详解如何使用PM2将Node.js的集群变得更加容易
Nov 15 Javascript
解决vue打包之后静态资源图片失效的问题
Feb 21 Javascript
vue 修改 data 数据问题并实时显示的方法
Aug 27 Javascript
JS localStorage存储对象,sessionStorage存储数组对象操作示例
Feb 15 Javascript
Antd表格滚动 宽度自适应 不换行的实例
Oct 27 Javascript
antdesign-vue结合sortablejs实现两个table相互拖拽排序功能
Jan 08 Vue.js
浅析AngularJS中的生命周期和延迟处理
Jun 18 #Javascript
Node.js事件驱动
Jun 18 #Javascript
详解AngularJS的通信机制
Jun 18 #Javascript
javascript背景时钟实现方法
Jun 18 #Javascript
移动Web中图片自适应的两种JavaScript解决方法
Jun 18 #Javascript
javascript随机显示背景图片的方法
Jun 18 #Javascript
利用JavaScript的AngularJS库制作电子名片的方法
Jun 18 #Javascript
You might like
php 获取页面中指定内容的实现类
2014/01/23 PHP
CentOS6.5 编译安装lnmp环境
2014/12/21 PHP
PHP+ajax分页实例简析
2015/12/07 PHP
php打开本地exe程序,js打开本地exe应用程序,并传递相关参数方法
2018/02/06 PHP
php生成微信红包数组的方法
2019/09/05 PHP
短信提示使用 特效
2007/01/19 Javascript
扩展jQuery 键盘事件的几个基本方法
2009/10/30 Javascript
javascript动态加载二
2012/08/22 Javascript
js中的for如何实现foreach中的遍历
2014/05/31 Javascript
jQuery中on()方法用法实例
2015/01/19 Javascript
AngularJS 最常用的功能汇总
2016/02/17 Javascript
jquery trigger函数执行两次的解决方法
2016/02/29 Javascript
JS随机洗牌算法之数组随机排序
2016/03/23 Javascript
浅析JS动态创建元素【两种方法】
2016/04/20 Javascript
jQuery操作dom实现弹出页面遮罩层(web端和移动端阻止遮罩层的滑动)
2016/08/25 Javascript
input file样式修改以及图片预览删除功能详细概括(推荐)
2017/08/17 Javascript
jQuery+datatables插件实现ajax加载数据与增删改查功能示例
2018/04/17 jQuery
详解vue-cli3使用
2018/08/14 Javascript
Typescript 中的 interface 和 type 到底有什么区别详解
2019/06/18 Javascript
解决Vue项目打包后打开index.html页面显示空白以及图片路径错误的问题
2019/10/25 Javascript
three.js 利用uv和ThreeBSP制作一个快递柜功能
2020/08/18 Javascript
linux系统使用python监控apache服务器进程脚本分享
2014/01/15 Python
理解python正则表达式
2016/01/15 Python
python实现K近邻回归,采用等权重和不等权重的方法
2019/01/23 Python
python实现飞机大战游戏
2020/10/26 Python
python 实现方阵的对角线遍历示例
2019/11/29 Python
Pytorch 实现focal_loss 多类别和二分类示例
2020/01/14 Python
手把手教你将Flask应用封装成Docker服务的实现
2020/08/19 Python
CSS中的字体大小设置属性总结
2016/05/24 HTML / CSS
2014年学生党支部工作总结
2014/12/20 职场文书
试用期自我评价怎么写
2015/03/10 职场文书
公司辞职信模板
2015/05/13 职场文书
销售人员管理制度
2015/08/06 职场文书
医德医风学习心得体会
2016/01/25 职场文书
简述python四种分词工具,盘点哪个更好用?
2021/04/13 Python
JavaScript数组 几个常用方法总结
2021/11/11 Javascript