热门标签 | HotTags
当前位置:  开发笔记 > 开发工具 > 正文

详解Nodejs的timers模块

本篇文章主要介绍了Nodejs的timers模块,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

本模块,属于来模拟一些浏览器自带方法的模块,比如setTimeout,clearTimeout等方法,之所以会有该模块,在我看来,也是为了能让前端工程师使用起来,更简单,使用一个单独的模块,来把浏览器上的功能来模拟出来,那么就可以直接减少学习的成本,这样就可以花更少的时间,学习到更多的东西。

timers模块中,使用的C++的方法

timers模块中,调用了C++实现的方法,这些方法,在该模块中,占据了很重要的位置,所以,这里我们先来看下,在C++的方法中,提供了哪些方法。

var Timer = process.binding('timer_wrap').Timer; 
console.log(Timer); 

运行之后,在控制台,就会打印出如下的内容,它的格式如下

{ 
 [Function: Timer] 
 // Timer构造函数,可以进行实例化 
 kOnTimeout: 0, 
 // 静态属性,公用,更改会影响其他的调用 
 now: [Function: now] 
 // 静态方法,获取类似时间戳的一个数字 
} 

其中,Timer本身是一个构造函数,而这个构造函数中,还包含了一个静态属性和一个静态方法,关于静态属性和方法,基本上,这两个只是拿来使用的,是禁止修改的,并且,其使用方法比较简单,所以这里不多说了。
Timer既然还是一个构造函数,那么久是可以被实例化的,接下来,看下实例化之后的对象:

var Timer = process.binding('timer_wrap').Timer, 
 timer = new Timer(), 
 i = ""; 
 
console.log("obj has attribute:"); 
console.log(timer); 
 
console.log("prototype method and attribute:"); 
for(i in timer){ 
 console.log(i+"="+timer[i]); 
} 

把上面的代码,执行的结果如下:

obj has attribute: 
{} 
 
prototype method and attribute: 
close=function close() { [native code] } 
ref=function ref() { [native code] } 
unref=function unref() { [native code] } 
start=function start() { [native code] } 
stop=function stop() { [native code] } 
setRepeat=function setRepeat() { [native code] } 
getRepeat=function getRepeat() { [native code] } 
again=function again() { [native code] } 

从上面的结果中可以看出,在Timer实例化之后,在对象本身,是没有属性和方法的,在原型链上,是有一些方法,至于这些方法,有什么用,就需要慢慢去看一下了。

timers模块中的一个基础--构造函数Timeout

之所以这里要把这个构造函数以单小节的形式给出,是因为在我看来,如果想要对整个timers模块中的逻辑有更好的认识,那么该模块的基础一个私有的构造函数的理解,还是很有必要的。

这里,我们首先来看一下源码:

var Timeout = function(after) { 
 // 定义内部属性,过时时间 
 this._idleTimeout = after; 
 
 // 循环链表中的两个属性,可以参考前篇文章linklist私有模块 
 this._idlePrev = this; 
 this._idleNext = this; 
 
 // 记录开始计时时间的属性 
 this._idleStart = null; 
 
 // 当时间到了,执行的回调函数 
 this._OnTimeout= null; 
 
 // 该计时器,是否需要repeat,setInterval方法,该属性为true 
 this._repeat = false; 
}; 
 
 
function unrefdHandle() { 
 // unref方法的回调函数,内部this指向Timeout._handle属性 
 // 在该属性上,定义了owner属性,保存Timeout的实例化后的对象 
 this.owner._onTimeout(); 
 if (!this.owner._repeat) 
  this.owner.close(); 
} 
 
Timeout.prototype.unref = function() { 
 // 这个方法,是用来暂停计时器的 
 // 添加一个新的属性_handle用来对接C++提供的API接口 
 if (!this._handle) { 
  // 做一些初始的判断属性,设置初始值等 
  var now = Timer.now(); 
  if (!this._idleStart) this._idleStart = now; 
  var delay = this._idleStart + this._idleTimeout - now; 
  if (delay <0) delay = 0; 
 
  // 把this指向的计时器对象,清理掉,从计时器链表中清理掉 
  exports.unenroll(this); 
 
  // 介入C++提供的API方法 
  this._handle = new Timer(); 
 
  // 添加一些属性,用来保存一些信息 
  this._handle.owner = this; 
  this._handle[kOnTimeout] = unrefdHandle; 
 
  // 开始计时,在delay后执行改方法的回调 
  this._handle.start(delay, 0); 
  this._handle.domain = this.domain; 
 
  // 调用C++提供的方法,停止计时器的执行 
  this._handle.unref(); 
 } else { 
  // 如果之前有_handle属性,那么则直接停止 
  this._handle.unref(); 
 } 
}; 
 
Timeout.prototype.ref = function() { 
 // 该方法,只有在unref之后,才起作用,恢复计时器的工作 
 // 如果在unref中,生成了_handle属性,那么使用该属性 
 // 调用C++提供的API,ref,恢复计时器的运行 
 if (this._handle) 
  this._handle.ref(); 
}; 
 
Timeout.prototype.close = function() { 
 // 当要关闭计时器对象时,如果定义过接入C++饿API的方法时 
 // 直接使用C++的方法,关闭 
 // 否则,把该方法,清理出去 
 // 不让它再lists链表中,那么当计时器执行到时,也不会执行该计时器的回调函数 
 this._OnTimeout= null; 
 if (this._handle) { 
  this._handle[kOnTimeout] = null; 
  // 调用C++中提供的close方法,见前面构造函数Timer的原型链方法中 
  this._handle.close(); 
 } else { 
  exports.unenroll(this); 
 } 
}; 

上面的源码,就是在timers模块中,内部的一个私有构造函数,在timers公开的一些方法,占据了一个很重要的位子,因为,这个方法,是timers模块,与C++代码链接的重要部分。该部分,是没有示例可以给出的,只有在后面使用timers模块对外公开的API中,来看下对应的使用效果。

这里之所以,要先把这个构造函数放在这里,因为,在我看来,如果能先对这个构造函数有所了解的话,那么接下来看timers模块中的其他方法时,就会变的简单很多。

当然,也有可能是,因为没有看其他的源代码,而导致对于该构造函数的一些方法和属性,很没用感觉的,那么,接下来,就继续看下去吧。

timers模块的源码

timers中的源码,可以分为两部分,在这里,只会看下其中的一部分,还有另外一部分,是和延时执行相反的立即执行的回调函数,这是我们不常用到的,所以这里就不在占用篇幅。

这里,依然使用源码来开始:

'use strict'; 
 
// timer_wrap模块,为底层C++实现的模块 
var Timer = process.binding('timer_wrap').Timer; 
 // Timer在控制台打印出的数据如下: 
 // {[Function: Timer] 是一个构造函数 
 //  kOnTimeout: 0, 
 //  now: [Function: now] 
 // } 
 
// Nodejs模拟的双向链表的操作模块,请查看前一篇关于linklist的文章 
var L = require('_linklist'); 
 
// 断言的管理模块中的ok方法 
var assert = require('assert').ok; 
 
var kOnTimeout= Timer.kOnTimeout | 0; 
 
// Timeout values > TIMEOUT_MAX are set to 1. 
var TIMEOUT_MAX = 2147483647; // 2^31-1 
 
// 把timer添加到debug的模块中,并生成一个函数,命名为debug 
// 在之后,直接调用,该函数,即可把官员timer的错误信息,打印到控制台 
var util = require('util'); 
var debug = util.debuglog('timer'); 
// 注,debuglog方法,应该是最近的版本中,新添加的,因为在一年前,刚接触nodejs时, 
// util模块中,还没有该方法 
 
 
// Object containing all lists, timers 
// key = time in milliseconds 
// value = list 
var lists = {}; 
 
// the main function - creates lists on demand and the watchers associated 
// with them. 
// 把item存入到一个链表中去,并且把msecs对应的链表,存入到lists对象中去 
// lists的格式是这样的: 
 // { 
 // "1000": 这里是一个循环链表,该链表内,包含了所有msecs=1000的list对象 
 // "2000":{} 
 // } 
 
function insert(item, msecs) { 
 // 给item定义两个私有属性 
 // 一个记录当前时间 
 item._idleStart = Timer.now(); 
 // 一个记录毫秒时间,类似于过期时间 
 item._idleTimeout = msecs; 
 
 // 如果定义的毫秒,是负值,则直接返回,不做后面的处理 
 if (msecs <0) return; 
 
 var list; 
 
 // 如果该过期时间,已经缓存在了lists对象中,则直接找到缓存的数据 
 if (lists[msecs]) { 
  list = lists[msecs]; 
 } else { 
  // 否则,执行新建一个list数据 
  // 并把item和msecs的数据初始化到新创建的对象中去 
  list = new Timer(); 
  // 下面这些,就是Timer实例化之后,包含的方法 
  // close 
  // ref 
  // unref 
  // start 
  // stop 
  // setRepeat 
  // getRepeat 
  // again 
 
  // 实例化之后,调用start方法 
  list.start(msecs, 0); 
 
  // 把list对象,改为一个循环链表 
  L.init(list); 
 
  // 把该list添加到lists对象中缓存 
  // 并设置一些属性,这些属性,在其他方法中被用到 
  lists[msecs] = list; 
  list.msecs = msecs; 
  list[kOnTimeout] = listOnTimeout; 
 } 
 
 // 把item插入到list的下一个节点去 
 L.append(list, item); 
 assert(!L.isEmpty(list)); // list is not empty 
} 
 
// 每一个list的kOnTimeout的属性值,应该是一个回调函数 
// 所以,其内部指向的是list本事 
function listOnTimeout() { 
 var msecs = this.msecs; 
 var list = this; 
 
 debug('timeout callback %d', msecs); 
 
 // 类似一个时间戳,但是又和Date.now()的毫秒级时间戳不同,不知道是如何判断这个的 
 var now = Timer.now(); 
 debug('now: %d', now); 
 
 var diff, first, threw; 
 
 // 当时间到了之后,把对应该时间的链表中的所有元素执行 
 // 如果出现异味,则等一会再次执行,请看源码中的具体注释 
 while (first = L.peek(list)) { 
  // If the previous iteration caused a timer to be added, 
  // update the value of "now" so that timing computations are 
  // done correctly. See test/simple/test-timers-blocking-callback.js 
  // for more information. 
  // 本处的while是,把list的所有前置列表,都处理一遍,直到list所处的链表中,只有list时结束 
  if (now  TIMEOUT_MAX) { 
  msecs = TIMEOUT_MAX; 
 } 
 
 // 设置信息,并初始化item本身的链表 
 item._idleTimeout = msecs; 
 L.init(item); 
}; 
 
 
// call this whenever the item is active (not idle) 
// it will reset its timeout. 
exports.active = function(item) { 
 // 把item插入到缓存的lists对象中, 
 // 或者把已经存在的于对象中的item,进行一次数据更新 
 var msecs = item._idleTimeout; 
 if (msecs >= 0) { 
  // 看上面的函数,enroll可以知道,msecs是必须大于等于0的 
  var list = lists[msecs]; 
  // 如果list存在于lists中,找到对应的链表 
 
  if (!list || L.isEmpty(list)) { 
   // 如果list为空,或者list为空链接,则执行insert方法,创建一个新的链表 
   // 并且把该链表,保存到lists[msecs]中去 
   insert(item, msecs); 
  } else { 
   // 如果有,那么更新item属性的当前时间,把item插入到list链表中去 
   item._idleStart = Timer.now(); 
   L.append(list, item); 
  } 
 } 
}; 
 
 
/* 
* DOM-style timers 
*/ 
 
 
exports.setTimeout = function(callback, after) { 
 // setTimeout的实现源代码 
 // 前两个参数必须是固定的 
 var timer; 
 
 // after转化为数字,或者NaN 
 after *= 1; // coalesce to number or NaN 
 
 // 如果不在合法范围之内,则把after设置为1 
 if (!(after >= 1 && after <= TIMEOUT_MAX)) { 
  after = 1; // schedule on next tick, follows browser behaviour 
 } 
 
 // 根据Timerout构造函数,生成一个实例,该构造函数完成的功能 
 // 只是创建一个对象,设置了一些属性和一些方法 
 timer = new Timeout(after); 
 // 实例化后的timer包含以下内部属性 
 // _idleTimeout = after; 
 // _idlePrev = this; 
 // _idleNext = this; 
 // _idleStart = null; 
 // _OnTimeout= null; 
 // _repeat = false; 
 // 以及一下几个原型链方法 
 // unref 
 // ref 
 // close 
 
 // 如果传入的参数,小于等于2个,说明没有多余的默认参数传入 
 if (arguments.length <= 2) { 
  timer._OnTimeout= callback; 
 } else { 
 
  // 如果有多余的默认参数传入,那么就要把多余的参数缓存一下 
  // 使用闭包,重新设置一个回调函数 
  var args = Array.prototype.slice.call(arguments, 2); 
  timer._OnTimeout= function() { 
   callback.apply(timer, args); 
  } 
 } 
 
 // 设置timer的domain属性为process的domain属性 
 // 该属性,暂时还不知道为什么存在 
 if (process.domain) timer.domain = process.domain; 
 
 // 把timer设置为启动,并在active中,插入到等待执行的列表中去 
 exports.active(timer); 
 
 // 返回Timeout的实例对象,所以,可以想象setTimeout的返回值,到底有哪些属性和方法了吧 
 return timer; 
}; 
 
 
exports.clearTimeout = function(timer) { 
 // 只有timer存在 
 // 回调存在,回调时间存在的情况下,才需要把该方法清理掉 
 // 至于为什么要判断这些条件,请参考listOnTimeout方法内部的注释及逻辑 
 if (timer && (timer[kOnTimeout] || timer._onTimeout)) { 
  timer[kOnTimeout] = timer._OnTimeout= null; 
  // 清除回调,时间等属性,然后把timer自lists链表中,去除掉 
  // 这样减少在每次调用时,对lists中对象的无意义的循环 
  if (timer instanceof Timeout) { 
   timer.close(); // for after === 0 
  } else { 
   exports.unenroll(timer); 
  } 
 } 
}; 
 
 
exports.setInterval = function(callback, repeat) { 
 // 前期的处理,和setTimeout方法相同,唯一不同的是,回调 
 // 在本方法中,回调之后,再添加另外一个计时器 
 // 在我看来,就像是每次去调用setTimeout方法一样 
 
 repeat *= 1; // coalesce to number or NaN 
 
 if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) { 
  repeat = 1; // schedule on next tick, follows browser behaviour 
 } 
 
 var timer = new Timeout(repeat); 
 var args = Array.prototype.slice.call(arguments, 2); 
 timer._OnTimeout= wrapper; 
 timer._repeat = true; 
 
 if (process.domain) timer.domain = process.domain; 
 exports.active(timer); 
 
 return timer; 
 
 function wrapper() { 
  callback.apply(this, args); 
  // If callback called clearInterval(). 
  if (timer._repeat === false) return; 
  // If timer is unref'd (or was - it's permanently removed from the list.) 
 
  // 下面的处理,是因为在net模块中,和在本模块中,重新启用一个计时器的方法有区别 
  if (this._handle) { 
   // 该分支处理,应该是为了net模块中做的处理 
   // 在本模块中,暂时是没有提及到该属性的 
   this._handle.start(repeat, 0); 
  } else { 
   // 当前的模块中的回调函数 
   timer._idleTimeout = repeat; 
   exports.active(timer); 
  } 
 } 
}; 
 
exports.clearInterval = function(timer) { 
 // 基本上,就是只有timer和repeat属性存在的情况下 
 // 才表示timer对象,是出于Interval方法中, 
 // 这个时候,才去清理掉repeat属性,然后clearTimeout的方法 
 // 清理掉该计时器 
 if (timer && timer._repeat) { 
  timer._repeat = false; 
  clearTimeout(timer); 
 } 
}; 

timers中的源码,就是这样了,篇幅有限,本篇到这里就结束了,接下来的一篇关于timers模块的文章,将就本篇的源码,结合一些示例,进行一些说明。

总结

像这样的一些模块,感觉突然不知道怎么写了,如果整篇的去放这个源码,感觉这样的文章,完全没有意义的,这样的话,还是应该分开写的吧。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文基于对相关论文和开源代码的研究,详细介绍了LOAM(激光雷达里程计与建图)的工作原理,并对其关键技术进行了分析。 ... [详细]
  • 资源推荐 | TensorFlow官方中文教程助力英语非母语者学习
    来源:机器之心。本文详细介绍了TensorFlow官方提供的中文版教程和指南,帮助开发者更好地理解和应用这一强大的开源机器学习平台。 ... [详细]
  • 构建基于BERT的中文NL2SQL模型:一个简明的基准
    本文探讨了将自然语言转换为SQL语句(NL2SQL)的任务,这是人工智能领域中一项非常实用的研究方向。文章介绍了笔者在公司举办的首届中文NL2SQL挑战赛中的实践,该比赛提供了金融和通用领域的表格数据,并标注了对应的自然语言与SQL语句对,旨在训练准确的NL2SQL模型。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文介绍如何使用 Sortable.js 库实现元素的拖拽和位置交换功能。Sortable.js 是一个轻量级、无依赖的 JavaScript 库,支持拖拽排序、动画效果和多种插件扩展。通过简单的配置和事件处理,可以轻松实现复杂的功能。 ... [详细]
  • 探讨一个显示数字的故障计算器,它支持两种操作:将当前数字乘以2或减去1。本文将详细介绍如何用最少的操作次数将初始值X转换为目标值Y。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 本文详细介绍了如何在 Spring Boot 应用中通过 @PropertySource 注解读取非默认配置文件,包括配置文件的创建、映射类的设计以及确保 Spring 容器能够正确加载这些配置的方法。 ... [详细]
  • This document outlines the recommended naming conventions for HTML attributes in Fast Components, focusing on readability and consistency with existing standards. ... [详细]
  • 在现代网络环境中,两台计算机之间的文件传输需求日益增长。传统的FTP和SSH方式虽然有效,但其配置复杂、步骤繁琐,难以满足快速且安全的传输需求。本文将介绍一种基于Go语言开发的新一代文件传输工具——Croc,它不仅简化了操作流程,还提供了强大的加密和跨平台支持。 ... [详细]
  • 解决微信电脑版无法刷朋友圈问题:使用安卓远程投屏方案
    在工作期间想要浏览微信和朋友圈却不太方便?虽然微信电脑版目前不支持直接刷朋友圈,但通过远程投屏技术,可以轻松实现在电脑上操作安卓设备的功能。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • Android LED 数字字体的应用与实现
    本文介绍了一种适用于 Android 应用的 LED 数字字体(digital font),并详细描述了其在 UI 设计中的应用场景及其实现方法。这种字体常用于视频、广告倒计时等场景,能够增强视觉效果。 ... [详细]
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 网络运维工程师负责确保企业IT基础设施的稳定运行,保障业务连续性和数据安全。他们需要具备多种技能,包括搭建和维护网络环境、监控系统性能、处理突发事件等。本文将探讨网络运维工程师的职业前景及其平均薪酬水平。 ... [详细]
author-avatar
keyu5182702936453
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有