nodejs源码_nodejs之setTimeout源码解析

setTimeout是在系统启动的时候挂载的全局函数。代码在timer.js。

function setupGlobalTimeouts() {const timers = NativeModule.require('timers');global.clearImmediate = timers.clearImmediate;global.clearInterval = timers.clearInterval;global.clearTimeout = timers.clearTimeout;global.setImmediate = timers.setImmediate;global.setInterval = timers.setInterval;global.setTimeout = timers.setTimeout;}

我们先看一下setTimeout函数的代码。

function setTimeout(callback, after, arg1, arg2, arg3) {if (typeof callback !== 'function') {throw new errors.TypeError('ERR_INVALID_CALLBACK');}var i, args;switch (arguments.length) {// fast casescase 1:case 2:break;case 3:args = [arg1];break;case 4:args = [arg1, arg2];break;default:args = [arg1, arg2, arg3];for (i = 5; i < arguments.length; i++) {// extend array dynamically, makes .apply run much faster in v6.0.0args[i - 2] = arguments[i];}break;}// 新建一个对象,保存回调,超时时间等数据,是超时哈希队列的节点const timeout = new Timeout(callback, after, args, false, false);// 启动超时器active(timeout);// 返回一个对象return timeout;
}

其中Timeout函数在lib/internal/timer.js里定义。

function Timeout(callback, after, args, isRepeat, isUnrefed) {after *= 1; // coalesce to number or NaNthis._called = false;this._idleTimeout = after;this._idlePrev = this;this._idleNext = this;this._idleStart = null;this._onTimeout = null;this._onTimeout = callback;this._timerArgs = args;this._repeat = isRepeat ? after : null;this._destroyed = false;this[unrefedSymbol] = isUnrefed;this[async_id_symbol] = ++async_id_fields[kAsyncIdCounter];this[trigger_async_id_symbol] = getDefaultTriggerAsyncId();if (async_hook_fields[kInit] > 0) {emitInit(this[async_id_symbol],'Timeout',this[trigger_async_id_symbol],this);}
}

由代码可知,首先创建一个保存相关信息的对象,然后执行active函数。

const active = exports.active = function(item) {// 插入一个超时对象到超时队列insert(item, false);
}
function insert(item, unrefed, start) {// 超时时间const msecs = item._idleTimeout;if (msecs < 0 || msecs === undefined) return;// 如果传了start则计算是否超时时以start为起点,否则取当前的时间if (typeof start === 'number') {item._idleStart = start;} else {item._idleStart = TimerWrap.now();}// 哈希队列const lists = unrefed === true ? unrefedLists : refedLists;var list = lists[msecs];// 没有则新建一个队列if (list === undefined) {debug('no %d list was found in insert, creating a new one', msecs);lists[msecs] = list = new TimersList(msecs, unrefed);}...// 把超时节点插入超时队列L.append(list, item);assert(!L.isEmpty(list)); // list is not empty
}

从上面的代码可知,active一个定时器实际上是把新建的timeout对象挂载到一个哈希队列里。我们看一下这时候的内存视图。

d537f1974c7135fd93d6d06ac83f1d8a.png

当我们创建一个timerList的是时候,就会关联一个底层的定时器,执行setTimeout时传进来的时间是一样的,都会在一条队列中进行管理,该队列对应一个定时器,当定时器超时的时候,就会在该队列中找出超时节点。下面我们看一下new TimeWraper的时候发生了什么。

TimerWrap(Environment* env, Local<Object> object) : HandleWrap(env, object,reinterpret_cast<uv_handle_t*>(&handle_),AsyncWrap::PROVIDER_TIMERWRAP) {int r = uv_timer_init(env->event_loop(), &handle_);CHECK_EQ(r, 0);}

其实就是初始化了一个libuv的uv_timer_t结构体。然后接着start函数做了什么操作。

static void Start(const FunctionCallbackInfo<Value>& args) {TimerWrap* wrap = Unwrap<TimerWrap>(args.Holder());CHECK(HandleWrap::IsAlive(wrap));int64_t timeout = args[0]->IntegerValue();int err = uv_timer_start(&wrap->handle_, OnTimeout, timeout, 0);args.GetReturnValue().Set(err);}

就是启动了刚才初始化的定时器。并且设置了超时回调函数是OnTimeout。这时候,就等定时器超时,然后执行OnTimeout函数。所以我们继续看该函数的代码。

const uint32_t kOnTimeout = 0;static void OnTimeout(uv_timer_t* handle) {TimerWrap* wrap = static_cast<TimerWrap*>(handle->data);Environment* env = wrap->env();HandleScope handle_scope(env->isolate());Context::Scope context_scope(env->context());wrap->MakeCallback(kOnTimeout, 0, nullptr);}

OnTimeout函数继续调kOnTimeout,但是该变量在time_wrapper.c中是一个整形,这是怎么回事呢?这时候需要回lib/timer.js里找答案。

const kOnTimeout = TimerWrap.kOnTimeout | 0;
// adds listOnTimeout to the C++ object prototype, as
// V8 would not inline it otherwise.
// 在TimerWrap中是0,给TimerWrap对象挂一个超时回调,每次的超时都会执行该回调
TimerWrap.prototype[kOnTimeout] = function listOnTimeout() {// 拿到该底层定时器关联的超时队列,看TimersListvar list = this._list;var msecs = list.msecs;//if (list.nextTick) {list.nextTick = false;process.nextTick(listOnTimeoutNT, list);return;}debug('timeout callback %d', msecs);var now = TimerWrap.now();debug('now: %d', now);var diff, timer;// 取出队列的尾节点,即最先插入的节点,最可能超时的,TimeOut对象while (timer = L.peek(list)) {diff = now - timer._idleStart;// Check if this loop iteration is too early for the next timer.// This happens if there are more timers scheduled for later in the list.// 最早的节点的消逝时间小于设置的时间,说明还没超时,并且全部节点都没超时,直接返回if (diff < msecs) {// 算出最快超时的节点还需要多长时间超时var timeRemaining = msecs - (TimerWrap.now() - timer._idleStart);if (timeRemaining < 0) {timeRemaining = 1;}// 重新设置超时时间this.start(timeRemaining);debug('%d list wait because diff is %d', msecs, diff);return;}// The actual logic for when a timeout happens.// 当前节点已经超时    L.remove(timer);assert(timer !== L.peek(list));if (!timer._onTimeout) {if (async_hook_fields[kDestroy] > 0 && !timer._destroyed &&typeof timer[async_id_symbol] === 'number') {emitDestroy(timer[async_id_symbol]);timer._destroyed = true;}continue;}// 执行超时处理tryOnTimeout(timer, list);}

由上可知,TimeWrapper.c里的kOnTimeout字段已经被改写成一个函数,所以底层的定时器超时时会执行上面的代码,即从定时器队列中找到超时节点执行,直到遇到第一个未超时的节点,然后重新设置超时时间。再次启动定时器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/537329.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

百度网盘PC端缓存文件夹

在C:\Users\zengm\AppData\Roaming\baidu\BaiduNetdisk\users\下面 BaiduYunCacheFileV0.db 文件为百度网盘目录数据&#xff0c;结构为&#xff1a; 百度网盘BaiduYunCacheFileV0.db数据库研究_wqq1027的博客-CSDN博客_百度网盘数据库最近研究了一下百度网盘的本地数据库文件…

python 图片转文字错误_python3把base64字符串写成图片文件出错

下面的代码在python2下正常的&#xff0c;是一个微信图标&#xff0c;文件md5是a1be719025844a1918ec6a338eaa8456我对python3不熟悉&#xff0c;不知道要怎么改#!/usr/bin/python3import base64def filePutContents(file, content):fp open(file, a)fp.write(content)fp.clos…

从业回忆录,最后悔的事

被一篇文章误导 我清楚地记得,在我毕业第一年,我看到了一篇关于程序员怎么学技术的文章,观点是程序员要多学技术,文章引用了典故:“高筑墙,广积粮,缓称王”。当时读这篇文章,感觉很有道理,认同了文章里的观点。 这么些年,学了不少技术:C#、Asp.net、Java Web套餐、A…

kodexplorer开源网盘php程序配置解析

config/setting_user.php 追加内容&#xff08;一下都是&#xff0c;注意不要使用中文引号、双引号及分号&#xff09; //【指定多语言只保留中文】 $GLOBALS[config][settings][language] zh-CN; //【自定义群组创建时自动新建的目录】 $GLOBALS[config][settingSystemDefaul…

实现三元组表示的两个稀疏矩阵的加法_K-BERT | 基于知识图谱的语言表示模型

1.研究背景BERT曾被应用在多项NLP任务中&#xff0c;并且取得了很好的结果。它通过在大规模开放语料库上进行预训练以获得通用的语言表示&#xff0c;然后在特定的下游任务中进行微调&#xff0c;吸收特定领域的知识。但这些模型在不同的领域执行知识驱动任务时&#xff0c;效果…

Excel单元格“删除线”的添加与删除

软件&#xff1a;windows&#xff0c;WPS 点击字体设计的小角标&#xff0c;进入更多设置&#xff0c;勾选“删除线”

excel 表格复制到word后,宽超出word如何调整?

网上很多方法是用“选择性粘贴----excel表格对象”&#xff0c;这个适用表格行数少的&#xff0c;不超过一页word的。 步骤 复制到word里后&#xff0c;选中表格&#xff0c;右键---自动调整--选择具体的调整方式。 调整后效果

sql 查询上个月的数据_数据分析-SQL 进阶篇 多表查询

知识点一、表的加法Union&#xff1a;删除表中的重复值union al&#xff1a;包含表中所有内容&#xff0c;包括重复值二、表的联结联结&#xff1a;join联结分为以下五种&#xff1a;交叉联结&#xff08;cross join&#xff09;又称为笛卡尔积&#xff1a;将表中的每一行与另外…

jenkins部署三种构建方式的详细步骤

部署背景&#xff1a;jenkins&#xff1a; CentOS 7.4C IP&#xff1a;172.16.3.74gitlab-11.5.3&#xff1a; CentOS 7.4D IP&#xff1a;172.16.4.74此上部署都是根据我之前的博客配置完成的&#xff1b;jenkins有三种构建方…

从业回忆,一次大胆的冒险,程序员转岗项目经理

有些事不必知道得太早 程序员这个行业,被“中年危机”言论导向后,就和洗脚城女技师差不多,年轻,漂亮,技术好,体力好的技师收入高,一边拿着高薪,赚着外快,一边吐槽是青春饭,经常熬夜,干不长久。 2010年之前,网上宣传程序员是青春饭,程序员中年危机的文章很少。近几…

python中控脚本_Python3.7实现中控考勤机自动连接

由于考勤机与oa对接&#xff0c;oa会在每天定时取考勤机数据&#xff0c;但是需要考勤机是连接状态&#xff0c;所以搜索了下相关教程&#xff0c;写了个脚本自动连接。完全是个python小白&#xff0c;代码烂&#xff0c;仅作为笔记。理论上支持所有程序的每天自动开启与关闭# …

word转freemarker和修改的步骤

第一步 编写好word&#xff0c;用freemarker语法替换内容 第二步 word另存为 word xml 第三步 修改后缀名为&#xff1a;.ftl 第四步 notepad XML插件美化显示&#xff0c;插件安装教程&#xff1a;Notepad 7.6以上版本安装XML Tools插件_lingquan0809的博客-CSDN博客Note…

python编程首选_为什么说学编程首选是python

为什么学编程你可能不会成为一名专业的程序员&#xff0c; 不过学编程的确是有很多的原因的1. 最重要的是你想学&#xff01;不论是因为业余爱好还是作为职业&#xff0c;编程都是十分有意思的&#xff0c; 都会让你收获很多2. 如果你是对计算机感兴趣的&#xff0c; 想更多的了…

程序员的职业规划,程序员的未来趋势

作者2008年入行,至今15个年头,从程序员到管理。当初一起入行的,当初带我的技术大佬,也都转管理了。 回顾 2008年-2015年:程序员,会ASP.NET、Java、JSP Servlet、SSH、SSM、Mysql、Oracle、Sqlserver、DB2、Sql、H5、Javascript、Jquery、Bootstrap、easy-UI、Android、…

vue-cli 使用better-scroll

better-scroll api文档https://ustbhuangyi.github.io/better-scroll/doc/zh-hans/ 一&#xff1a;安装better-scroll 插件 cnpm install better-scroll --save 二&#xff1a;引入 import Bscroll from better-scroll 三&#xff1a;使用 页面结构要符合这样的&#xff0c;不…

逐行粒度的vuex源码分析

vuex源码分析 了解vuex 什么是vuex vuex是一个为vue进行统一状态管理的状态管理器&#xff0c;主要分为state, getters, mutations, actions几个部分&#xff0c;vue组件基于state进行渲染&#xff0c;当state发生变化时触发组件的重新渲染&#xff0c;并利用了vue的响应式原理…

Redis设计与实现之事件

目录 一、事件 1、文件事件 读事件 写事件 2、 时间事件 3、时间事件应用实例:服务器常规操作 4、事件的执行与调度 5、事件是否有重要性级别或优先级&#xff1f;需要立即处理还是可以延迟处理&#xff1f; 6、事件的类型是什么&#xff1f;是针对键的操作&#xff0…

如何用python32位开发词典软件_Python如何实现字典?

I was wondering how python dictionaries work under the hood, particularly the dynamic aspect?When we create a dictionary, what is its initial size?If we update it with a lot of elements, I suppose we need to enlarge the hash table. I suppose we need to r…

信息系统项目管理师:软件测试、调试及其管理

1&#xff0e;4&#xff0e;5软件测试及其管理 1、软件测试方法可分为静态测试和动态测试。 静态测试是指被测试程序不在机器上运行&#xff0c;而采用人工检测和计算机辅助静态分析的手段对程序进行检测。静态测试包括对文档的静态测试和对代码的静态测试。对文档的静态测试…

项目验收材料整合流程

目标&#xff1a;多份word整合成一份项目验收材料 第一步&#xff1a;编写好word&#xff1b;准备好一份验收材料的封面与目录word 第二步&#xff1a;用WPS的word转PDF&#xff0c;批量转成PDF&#xff1b; 第三步&#xff1a;用Adobe Acrobat DC 合并转成的多个PDF成为一个…