You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionTimersList(msecs,unrefed){this._idleNext=this;// Create the list with the linkedlist properties tothis._idlePrev=this;// prevent any unnecessary hidden class changes.this._unrefed=unrefed;this.msecs=msecs;this.nextTick=false;consttimer=this._timer=newTimerWrap();timer._list=this;if(unrefed===true)timer.unref();timer.start(msecs);timer[kOnTimeout]=listOnTimeout;}
functioninsert(item,unrefed){constmsecs=item._idleTimeout;if(msecs<0||msecs===undefined)return;item._idleStart=TimerWrap.now();constlists=unrefed===true ? unrefedLists : refedLists;varlist=lists[msecs];if(list===undefined){debug('no %d list was found in insert, creating a new one',msecs);lists[msecs]=list=newTimersList(msecs,unrefed);}//other codes...L.append(list,item);assert(!L.isEmpty(list));}
大家可以看一下这个insert函数,其中实现了TimersList的实例化,其中:
constlists=unrefed===true ? unrefedLists : refedLists;varlist=lists[msecs];if(list===undefined){debug('no %d list was found in insert, creating a new one',msecs);lists[msecs]=list=newTimersList(msecs,unrefed);}
functioninsert(item,unrefed){constmsecs=item._idleTimeout;if(msecs<0||msecs===undefined)return;//...if(list===undefined){debug('no %d list was found in insert, creating a new one',msecs);lists[msecs]=list=newTimersList(msecs,unrefed);}//...}functionTimersList(msecs,unrefed){//...this._unrefed=unrefed;this.msecs=msecs;this.nextTick=false;//...}
前言
在阅读 ./lib/timers.js代码的时候,首先映入眼帘的便是如下这几行注释:
我们按照注释的引导,一点一点把整体的timers.js结构扒开,看它究竟做了什么。
timers中的双向链表
按照注释中的引子,我们先来看一下TimersList究竟是什么样子的,目光直接移向下面这段代码:
这里是TimersList的实现代码,不管其他的,看到
this.xxxNext
和this.xxxPrev
,第一时间就会想到双向链表。没错,TimersList就是一个双向链表,那么它的两端分别连接的是什么呢?大家可以看一下这个insert函数,其中实现了TimersList的实例化,其中:
这几句话基本涵盖了刚才注释里面的
refedLists
以及TimersList
,这里可以看出referedLists是一个对象,而对象的key是变量msecs, value则对应一个TimersList。之后的
L.append(list, item);
,L是来自于./lib/internal/linkedlist.js中操作双向链表的方法,append则是在链表后面插入一个元素,而list是实例化的TimersList,所以也就是在TimersList后插入新的item,到此为止,整体结构如下所示:msecs是什么
通过翻阅./lib/timers.js的代码可以找到几个和msecs强相关的地方:
从这些代码可以看出来msecs的起源实际是item._idleTimeout,而item根据如下几个地方溯源:
直接跟进到./lib/internal/timers.js,
其中有这样一段代码:
所以,msesc其实是Timeout的第二个参数--after,即setTimeout API中的第二个参数,延迟几秒执行,_onTimeout就是最终要执行的callback
读完这个章节,我们的整体结构可以修改成:
TimersList的TimerWrap
这里我们要从一个细节入手,在定义TimersList的地方,有一行代码:
从这里我们可以看到TimersList有一个属性_timer,而这个_timer的来源又很隐蔽,是通过binding方法挂载进来的内置api。在这种情况下,矛头直接指向./src/timer_wrap.cc中一探究竟。
可以看到,在node中构造了timer_wrap的构造函数,其中包含了若干方法和属性,在这里我们先重点看一下start方法:
首先,TimerWrap会执行
uv_timer_init(env->event_loop(), &handle_)
。uv_timer_init为libuv中定时器的初始化函数,它的参数有两个:第一个参数是定时器初始化时候的主event_loop,第二个参数为指向uv_timer_t的指针。一般以handle命名(因为uv_timer_t为uv_handle_t的子类,uv_handle_t的结构体声明中又包含了uv_loop_t),handle相当于uv_timer_start之后的唯一标识,如下我所了解到的一些用法:我对libuv也不是十分了解,如上是我大致了解的handle的用途,还希望大家给予补充。
uv_timer_start四个参数的意义可以在 ./deps/uv/src/unix/timer.c中找到,分别为:handle、回调、延迟执行的时间、是否重复执行。
问题马上迎刃而解了,下面我们只需要关注一下OnTimeout:
在这里有一个比较重要的过程,就是
handle->data
,这里的handle指向的是uv_timer_t,而这个data则是通过node中的HandleWrap传递给libuv的handle_warp.cc:所以接下来就可以通过
wrap->MakeCallback
调用MakeCallback实现对js函数的调用。通过这一章节的叙述,可以基本缕清refedLists的整体结构:
执行阶段
沿用刚才的逻辑,其实暴露出来的
timer.start
方法就会调用uv_timer_start
来启动定时循环处理,由此可见,在TimersList中:随着每一个TimersList的注册,会启动uv_timer_start,来开始整体的一个事件循环,拿上面的总结举例子来说:
当refedLists中注册了10毫秒后需要执行的TimersList的时候,旋即启动uv_timer_start,每隔十秒执行
timer[kOnTimeout] = listOnTimeout
即listOnTimeout,下面是整个完整的回调函数调用链:刚才经过分析,大家应该还对
_onTimeout
有印象吧?没错,这就是最后要执行的callback,也就是listOnTimeout
最终执行了setTimeout
方法的callback。至此,整个timers的整体流程就分析结束了。by 小菜
The text was updated successfully, but these errors were encountered: