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.pswp.cn/news/537329.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537329.shtml
英文地址,請注明出處:http://en.pswp.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成為一個…