亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

詳解Nodejs的timers模塊

2019-11-19 18:18:50
字體:
來源:轉載
供稿:網友

本模塊,屬于來模擬一些瀏覽器自帶方法的模塊,比如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 < first._idleStart) {    // 當first元素,當執行insert時,會操作_idleStart的屬性值    // 如果Timer.now的值,是一直增加的,那么這里為神馬會執行?    // 那么又為什么要有這個判斷?只是打了一個log,難道只是為了做個通知?    now = Timer.now();    debug('now: %d', now);   }    // 求這個差值?并且與list的msecs值進行判斷   diff = now - first._idleStart;   if (diff < msecs) {    // 執行到這里,那邊把list繼續延時一段時間,因為當前的一個item沒有被執行    // 所以重新計時,再執行一次    list.start(msecs - diff, 0);    debug('%d list wait because diff is %d', msecs, diff);     // 并且直接return,結束本回調函數,等待msecs-diff時間之后,再次執行    return;   } else {    // 把first從它所在的鏈表中移除    L.remove(first);     // 我覺得,這里是在判斷,是否移除成功    assert(first !== L.peek(list));     // 如果當前的first沒有回調函數,那么不需要再向下執行,繼續while循環    if (!first._onTimeout) continue;     // 接下來,就是執行回調的處理了,處理的邏輯還行,看起來不算復雜    // 只是,有些判斷,我現在無法理解到,為什么要這么判斷     // v0.4 compatibility: if the timer callback throws and the    // domain or uncaughtException handler ignore the exception,    // other timers that expire on this tick should still run.    //    // https://github.com/joyent/node/issues/2631    var domain = first.domain;    if (domain && domain._disposed)     continue;     try {     if (domain)      domain.enter();     threw = true;     first._onTimeout();     if (domain)      domain.exit();     threw = false;    } finally {     if (threw) {      // We need to continue processing after domain error handling      // is complete, but not by using whatever domain was left over      // when the timeout threw its exception.      var oldDomain = process.domain;      process.domain = null;      process.nextTick(function() {       list[kOnTimeout]();      });      process.domain = oldDomain;     }    }   }  }   debug('%d list empty', msecs);  assert(L.isEmpty(list));  list.close();  delete lists[msecs]; }   var unenroll = exports.unenroll = function(item) {  L.remove(item);   // _idleTimeout中保存著msecs的值,  // 所有可以根據該屬性,直接找到該對象在lists中的緩存數據  // 不過,item的msecs中,也保存了list本身的msecs的   var list = lists[item._idleTimeout];  // if empty then stop the watcher  debug('unenroll');  if (list && L.isEmpty(list)) {   debug('unenroll: list empty');   // list調用C++的接口   list.close();   delete lists[item._idleTimeout];  }  // if active is called later, then we want to make sure not to insert again  item._idleTimeout = -1;  // 本方法,其實就是在清理一些默認的數據了  // 屬于,當一個方法執行完之后,把其對應的數據,都直接清理掉  };   // Does not start the time, just sets up the members needed. exports.enroll = function(item, msecs) {  // 給item重新設置一些屬性  // msecs的值,需要時number類型,并且有效的正整數和零  if (!util.isNumber(msecs)) {   throw new TypeError('msecs must be a number');  }   if (msecs < 0 || !isFinite(msecs)) {   throw new RangeError('msecs must be a non-negative finite number');  }   // if this item was already in a list somewhere  // then we should unenroll it from that  // 保證,item不會存在于兩個鏈表中,  // 比如,我最初把item設置為1000之后執行,那么item在lists[1000]所在的鏈表中  // 接下來,我又把item設置為2000之后執行,那么就要先吧item從原來的lists[1000]的鏈表中刪除  // 然后,添加到lists[2000]所指向的鏈表去  if (item._idleNext) unenroll(item);   // Ensure that msecs fits into signed int32  // 保證是在最大值之內的,否則,設置為一個系統設置的最大值  if (msecs > 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模塊的文章,將就本篇的源碼,結合一些示例,進行一些說明。

總結

像這樣的一些模塊,感覺突然不知道怎么寫了,如果整篇的去放這個源碼,感覺這樣的文章,完全沒有意義的,這樣的話,還是應該分開寫的吧。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
尤物九九久久国产精品的分类| 国产成人avxxxxx在线看| 97av在线播放| 国产丝袜高跟一区| 超碰精品一区二区三区乱码| 国产午夜一区二区| 91在线观看免费观看| 国产成人精品一区二区三区| 久久综合网hezyo| 久久久久久国产| 免费99精品国产自在在线| 欧美最近摘花xxxx摘花| 国产精品福利在线观看| 成人免费看黄网站| 亚洲大胆人体视频| 欧美午夜视频一区二区| 精品国产美女在线| 国产mv免费观看入口亚洲| 久久精品国亚洲| **欧美日韩vr在线| 国产成人一区二区在线| 精品视频久久久久久久| 国产精品久久一| 国产一区二区成人| 欧美限制级电影在线观看| 日韩欧美一区二区三区| 美女视频黄免费的亚洲男人天堂| 成人黄色免费网站在线观看| 欧美日韩另类字幕中文| 久热精品视频在线| 45www国产精品网站| 精品日本美女福利在线观看| 亚洲天堂日韩电影| 日韩欧亚中文在线| 69**夜色精品国产69乱| 亚洲国产中文字幕久久网| 日本91av在线播放| 在线视频欧美日韩| 精品网站999www| 日本免费一区二区三区视频观看| 久久在线精品视频| 国产精品福利片| 亚洲偷熟乱区亚洲香蕉av| 在线视频精品一| 日韩在线观看免费| 亚洲午夜av久久乱码| 欧美日韩成人精品| 91久久久久久久一区二区| 中文字幕欧美视频在线| 亚洲视频欧洲视频| 色妞色视频一区二区三区四区| 成人av在线网址| 久久精品男人天堂| 亚洲最大的成人网| 色狠狠av一区二区三区香蕉蜜桃| 日韩成人中文电影| 国产成人亚洲综合91精品| 精品激情国产视频| 欧美激情视频在线免费观看 欧美视频免费一| 久久精品国产综合| 疯狂做受xxxx高潮欧美日本| 国产精品草莓在线免费观看| 国产一区二区av| 97在线精品国自产拍中文| 久久亚洲精品一区二区| 最近2019中文字幕mv免费看| 国产精品无av码在线观看| 91在线看www| 日韩美女在线看| 国产精品ⅴa在线观看h| 自拍偷拍免费精品| 国产精品一区二区女厕厕| 久久好看免费视频| 精品日韩美女的视频高清| 久久免费福利视频| www.亚洲人.com| 亚洲老板91色精品久久| 久久久av电影| 亚洲人永久免费| 色偷偷亚洲男人天堂| 日韩经典中文字幕| 亚洲国产精品国自产拍av秋霞| 久久亚洲精品网站| 一本一本久久a久久精品牛牛影视| 欧美日韩国产专区| 国产suv精品一区二区三区88区| 精品视频www| 国产中文字幕日韩| 国产精品一区二区三区久久久| 欧美中文字幕视频在线观看| 91久久精品国产91久久性色| 欧美一区二粉嫩精品国产一线天| 久久久精品亚洲| 亚洲欧美精品一区二区| 久久久久久久999| 欧美成人四级hd版| 日韩欧美在线中文字幕| 91在线观看免费高清完整版在线观看| 亚洲精品国产福利| 久久精品国产亚洲7777| 久久久国产精彩视频美女艺术照福利| 一本一本久久a久久精品综合小说| 少妇高潮久久久久久潘金莲| 亚洲免费电影一区| 国a精品视频大全| 欧美日韩亚洲国产一区| 欧美在线中文字幕| 日韩av理论片| 亚洲在线观看视频网站| 97国产精品免费视频| 91在线网站视频| 久久激情视频免费观看| 亚洲午夜精品久久久久久久久久久久| 欧美成人精品xxx| 日韩成人激情视频| 欧洲中文字幕国产精品| 国产一区二区在线播放| 欧美性生交xxxxxdddd| 亚洲成av人乱码色午夜| 久久久成人的性感天堂| 亚洲成年人在线播放| 欧美性xxxx极品hd满灌| 午夜精品视频在线| 一区二区在线视频播放| 97人人爽人人喊人人模波多| 欧美日韩福利在线观看| 日韩一级裸体免费视频| 激情亚洲一区二区三区四区| 91精品国产综合久久香蕉| 日本成人在线视频网址| 97精品国产97久久久久久春色| 色悠悠久久久久| 久久影院资源网| 91精品视频观看| 人九九综合九九宗合| 亚洲精品国产精品国自产在线| 91久久久久久国产精品| 日韩av男人的天堂| 日韩高清电影免费观看完整版| 成人两性免费视频| 九九热99久久久国产盗摄| 国产精品自拍偷拍视频| 久久久久久久久久国产精品| 在线不卡国产精品| 久久久久久久av| 国产精品第1页| 久久99精品国产99久久6尤物| 欧美精品情趣视频| 日韩欧美第一页| 久久久久久尹人网香蕉| 国产91精品久久久久久| 亚洲成人网在线| 亚洲精品国产福利| 亚洲精品综合久久中文字幕| 一区二区av在线| 国产精品伦子伦免费视频| 欧美日韩亚洲国产一区| 亚洲欧美国内爽妇网| 国产狼人综合免费视频| 成人亲热视频网站| 久久精品欧美视频| 亚洲天堂男人天堂女人天堂| 57pao成人永久免费视频| 久久久亚洲欧洲日产国码aⅴ|