日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

深入解析Node.js setTimeout方法的执行过程

發(fā)布時(shí)間:2024/7/5 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入解析Node.js setTimeout方法的执行过程 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

深入了解setTimeout源碼之前,本有兩個(gè)選擇。一是通過(guò)chromium源碼分析,二是通過(guò)Node.js源碼分析。后來(lái)發(fā)現(xiàn)第一種方案的源碼獲取成本太大,于是從Node官網(wǎng)獲取了幾十兆的代碼用來(lái)了解。

當(dāng)前的Node版本為:v10.16.0

setTimeout方法定義于timers.js文件中,源碼整體如下:

function setTimeout(callback, after, arg1, arg2, arg3) {// 基礎(chǔ)校驗(yàn)if (typeof callback !== 'function') {throw new ERR_INVALID_CALLBACK();}// 參數(shù)校驗(yàn)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;}// 新建Timeout對(duì)象const timeout = new Timeout(callback, after, args, false, false);active(timeout);// 最后返回Timeout對(duì)象return timeout; }

Timeout構(gòu)造函數(shù)內(nèi)部如下:

// Timer constructor function. // The entire prototype is defined in lib/timers.js function Timeout(callback, after, args, isRepeat, isUnrefed) {after *= 1; // coalesce to number or NaNif (!(after >= 1 && after <= TIMEOUT_MAX)) {if (after > TIMEOUT_MAX) {process.emitWarning(`${after} does not fit into` +' a 32-bit signed integer.' +'\nTimeout duration was set to 1.','TimeoutOverflowWarning');}after = 1; // schedule on next tick, follows browser behavior}this._called = false;this._idleTimeout = after;this._idlePrev = this;this._idleNext = this;this._idleStart = null;// this must be set to null first to avoid function tracking// on the hidden class, revisit in V8 versions after 6.2this._onTimeout = null;this._onTimeout = callback;this._timerArgs = args;this._repeat = isRepeat ? after : null;this._destroyed = false;this[unrefedSymbol] = isUnrefed;initAsyncResource(this, 'Timeout'); }

我們這里分析只關(guān)注兩個(gè)參數(shù):1.callback, 2.after

this._idleTimeout = after;this._onTimeout = callback;

基本初始化完成,進(jìn)入active方法。active方法的item參數(shù)為新new的Timeout對(duì)象。

// Schedule or re-schedule a timer. // The item must have been enroll()'d first. const active = exports.active = function(item) {insert(item, false); };

進(jìn)入insert方法,item = Timeout對(duì)象, unrefed = false, start = undefined.

// The underlying logic for scheduling or re-scheduling a timer. // // Appends a timer onto the end of an existing timers list, or creates a new // TimerWrap backed list if one does not already exist for the specified timeout // duration. function insert(item, unrefed, start) { // timeout, false // 對(duì)after做校驗(yàn)const msecs = item._idleTimeout;if (msecs < 0 || msecs === undefined) return;if (typeof start === 'number') {item._idleStart = start;} else {item._idleStart = TimerWrap.now();}const lists = unrefed === true ? unrefedLists : refedLists;// Use an existing list if there is one, otherwise we need to make a new one.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);}if (!item[async_id_symbol] || item._destroyed) {item._destroyed = false;initAsyncResource(item, 'Timeout');}L.append(list, item); // list = timerlist, item = timeout, 增加一個(gè)節(jié)點(diǎn)在隊(duì)列中,節(jié)點(diǎn)類(lèi)型不同。assert(!L.isEmpty(list)); // list is not empty }

TimerWrap.now()方法返回的應(yīng)當(dāng)是當(dāng)前的時(shí)間,具體的執(zhí)行代碼為:

static void Now(const FunctionCallbackInfo<Value>& args) {Environment* env = Environment::GetCurrent(args);args.GetReturnValue().Set(env->GetNow());}

這時(shí),Timeout對(duì)象有三個(gè)關(guān)鍵屬性:

item._idleTimeout = after; // 延遲多少秒執(zhí)行item._onTimeout = callback; // 延遲執(zhí)行回調(diào)函數(shù)item._idleStart = TimerWrap.now(); // 當(dāng)下時(shí)間

然后進(jìn)行到lists[after] = refedLists[after] = list = new TimersList(after, false);

也就是說(shuō)refedLists對(duì)象的after屬性對(duì)應(yīng)一個(gè)TimersList對(duì)象,而refedLists對(duì)象是全局的。

function TimersList(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;const timer = this._timer = new TimerWrap();timer._list = this;if (unrefed === true)timer.unref();timer.start(msecs); }

可以將TimersList對(duì)象視作為一個(gè)雙向鏈表節(jié)點(diǎn),它內(nèi)部有指向上下節(jié)點(diǎn)的指針,當(dāng)一個(gè)節(jié)點(diǎn)新建時(shí),這個(gè)節(jié)點(diǎn)的的上下節(jié)點(diǎn)會(huì)指向自己。節(jié)點(diǎn)的內(nèi)容為:

this.msecs = after;this._timer = new TimerWrap(); // 這里的TimerWrap為一個(gè)Native對(duì)象timer._list = this;

最后到timer.start(after),它的函數(shù)內(nèi)部如下:

static void Start(const FunctionCallbackInfo<Value>& args) {TimerWrap* wrap;ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());CHECK(HandleWrap::IsAlive(wrap));int64_t timeout = args[0]->IntegerValue(); // 這里的timeout為js代碼傳入的afterint err = uv_timer_start(&wrap->handle_, OnTimeout, timeout, 0);args.GetReturnValue().Set(err);}

這里關(guān)鍵的地方是uv_timer_start,方法的內(nèi)部如下:

int uv_timer_start(uv_timer_t* handle,uv_timer_cb cb,uint64_t timeout,uint64_t repeat) {uint64_t clamped_timeout;if (cb == NULL)return UV_EINVAL;if (uv__is_active(handle))uv_timer_stop(handle);// 這里是關(guān)鍵。clamped_timeout的值等于當(dāng)前時(shí)間加上未來(lái)要執(zhí)行的時(shí)間clamped_timeout = handle->loop->time + timeout;if (clamped_timeout < timeout)clamped_timeout = (uint64_t) -1;handle->timer_cb = cb;handle->timeout = clamped_timeout; // timeout為未來(lái)要執(zhí)行的時(shí)間handle->repeat = repeat;/* start_id is the second index to be compared in uv__timer_cmp() */handle->start_id = handle->loop->timer_counter++;heap_insert(timer_heap(handle->loop),(struct heap_node*) &handle->heap_node,timer_less_than);uv__handle_start(handle);return 0; }

這里我關(guān)注的是傳入?yún)?shù)是怎么被操作的:

handle->timer_cb = cb;handle->timeout = clamped_timeout;

好,到這里設(shè)置完成,我們回到insert方法內(nèi)部繼續(xù)向下,繼續(xù)執(zhí)行:

L.append(list, item); // list = timerlist, item = timeout, 增加一個(gè)節(jié)點(diǎn)在隊(duì)列中,節(jié)點(diǎn)類(lèi)型不同。

append方法將item追加到了list中。list對(duì)象是一個(gè)由item節(jié)點(diǎn)組成的雙向鏈表。然后到這里添加結(jié)束。

你可能會(huì)疑惑,到這里就結(jié)束了?其實(shí)過(guò)程中有很多細(xì)節(jié)被我們忽略了,不過(guò)沒(méi)關(guān)系。既然L.append用來(lái)追加節(jié)點(diǎn),那它一定要取出節(jié)點(diǎn),我們從上下文可知:

listOnTimeout方法中取出了這個(gè)節(jié)點(diǎn)(這個(gè)過(guò)程后面再涉及):

function listOnTimeout(handle, now) {const list = handle._list;const msecs = list.msecs;debug('timeout callback %d', msecs);debug('now: %d', now);var diff, timer;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;}handle.start(timeRemaining);debug('%d list wait because diff is %d', msecs, diff);return true;}// The actual logic for when a timeout happens.L.remove(timer);assert(timer !== L.peek(list));if (!timer._onTimeout) {if (destroyHooksExist() && !timer._destroyed &&typeof timer[async_id_symbol] === 'number') {emitDestroy(timer[async_id_symbol]);timer._destroyed = true;}continue;}tryOnTimeout(timer);}// If `L.peek(list)` returned nothing, the list was either empty or we have// called all of the timer timeouts.// As such, we can remove the list and clean up the TimerWrap C++ handle.debug('%d list empty', msecs);assert(L.isEmpty(list));// Either refedLists[msecs] or unrefedLists[msecs] may have been removed and// recreated since the reference to `list` was created. Make sure they're// the same instance of the list before destroying.if (list._unrefed === true && list === unrefedLists[msecs]) {delete unrefedLists[msecs];} else if (list === refedLists[msecs]) {delete refedLists[msecs];}// Do not close the underlying handle if its ownership has changed// (e.g it was unrefed in its callback).if (!handle[owner_symbol])handle.close();return true; }

listOnTimeout方法其實(shí)是整個(gè)JS層處理隊(duì)列事件的核心。方法內(nèi)部的handle對(duì)象實(shí)為T(mén)imerWrap。handle._list為T(mén)imersList。方法內(nèi)的msecs為after。接下來(lái)while不斷從TimersList中取timer。peek總是返回隊(duì)首的數(shù)據(jù)。

然后到了關(guān)鍵處理階段:

diff = now - timer._idleStart; // 計(jì)算方法執(zhí)行時(shí)的差額// 如果時(shí)間還不到,則進(jìn)行:if (diff < msecs) {var timeRemaining = msecs - (TimerWrap.now() - timer._idleStart);if (timeRemaining <= 0) {timeRemaining = 1;}// 計(jì)算出剩余時(shí)間,再次執(zhí)行Native的start方法。handle.start(timeRemaining);debug('%d list wait because diff is %d', msecs, diff);return true;}

handle.start方法的內(nèi)部如下:

static void Start(const FunctionCallbackInfo<Value>& args) {TimerWrap* wrap;ASSIGN_OR_RETURN_UNWRAP(&wrap, 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);}

這時(shí)在執(zhí)行start方法時(shí)就是一個(gè)after距離真正執(zhí)行的剩余時(shí)間,再次執(zhí)行uv_timer_start方法,

還記得上文中提到的這段代碼嗎?這段代碼位于uv_timer_start方法內(nèi)。

handle->timer_cb = cb;handle->timeout = clamped_timeout;

剛剛我們并沒(méi)有繼續(xù)深究,現(xiàn)在不得不深究一下。

上面說(shuō)到timeout被賦值了,那一定有地方再取出它進(jìn)行執(zhí)行。通過(guò)上下文可知:

void uv__run_timers(uv_loop_t* loop) {struct heap_node* heap_node;uv_timer_t* handle;for (;;) {heap_node = heap_min(timer_heap(loop));if (heap_node == NULL)break;handle = container_of(heap_node, uv_timer_t, heap_node);// 這里為處理的關(guān)鍵。if (handle->timeout > loop->time)break;uv_timer_stop(handle);uv_timer_again(handle);handle->timer_cb(handle);} }

uv__run_timers是一個(gè)無(wú)限循環(huán)的方法,內(nèi)部永遠(yuǎn)在循環(huán)執(zhí)行timer_cb方法。還記得上文中提到的clamped_timeout嗎,如果clamped_timeout為任務(wù)觸發(fā)的時(shí)間,這里的無(wú)限循環(huán)一直在判斷時(shí)間是否到期,如果到期了則會(huì)向下執(zhí)行,否則一直循環(huán)。

這個(gè)方法是如何被觸發(fā)的我們暫時(shí)不在這里深究。

我們從上文的代碼可以知道,上文中的timer_cb是OnTimeout方法,它的內(nèi)部實(shí)現(xiàn)如下:

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());MaybeLocal<Value> ret;Local<Value> args[1];do {// 這里是關(guān)鍵所在args[0] = env->GetNow(); // 獲取當(dāng)前時(shí)間ret = wrap->MakeCallback(env->timers_callback_function(), 1, args); // 執(zhí)行調(diào)用} while ((ret.IsEmpty() || ret.ToLocalChecked()->IsUndefined()) &&!env->tick_info()->has_thrown() &&env->can_call_into_js() &&wrap->object()->Get(env->context(),env->owner_symbol()).ToLocalChecked()->IsUndefined());}

這里的timers_callback_function()為js層傳入的processTimers方法,設(shè)置的代碼位于lib/timers.js文件中:

const [immediateInfo, toggleImmediateRef] =setupTimers(processImmediate, processTimers);

processTimers內(nèi)部如下:

function processTimers(now) {if (this[owner_symbol])return unrefdHandle(this[owner_symbol], now);return listOnTimeout(this, now); }

到這里是不是見(jiàn)過(guò)listOnTimeout方法?沒(méi)錯(cuò),我們回到了listOnTimeout方法調(diào)用處。這個(gè)從listOnTimeout方法到listOnTimeout方法會(huì)不斷循環(huán),直到if (diff < msecs) 條件不成立。也就是說(shuō)當(dāng)條件成立時(shí)才會(huì)繼續(xù)執(zhí)行。

真正的業(yè)務(wù)回調(diào)代碼如下:

// 將這次的timer任務(wù)從隊(duì)列中取出L.remove(timer);assert(timer !== L.peek(list));// 這里不成立if (!timer._onTimeout) {if (destroyHooksExist() && !timer._destroyed &&typeof timer[async_id_symbol] === 'number') {emitDestroy(timer[async_id_symbol]);timer._destroyed = true;}continue;}// 關(guān)鍵在于這里tryOnTimeout(timer); function tryOnTimeout(timer, start) {timer._called = true;const timerAsyncId = (typeof timer[async_id_symbol] === 'number') ?timer[async_id_symbol] : null;var threw = true;if (timerAsyncId !== null)emitBefore(timerAsyncId, timer[trigger_async_id_symbol]);try {ontimeout(timer, start);threw = false;} finally {if (timerAsyncId !== null) {if (!threw)emitAfter(timerAsyncId);if ((threw || !timer._repeat) && destroyHooksExist() &&!timer._destroyed) {emitDestroy(timerAsyncId);timer._destroyed = true;}}} }

這里的關(guān)鍵在于ontimeout方法,該方法內(nèi)部實(shí)現(xiàn)如下:

function ontimeout(timer, start) {const args = timer._timerArgs;if (typeof timer._onTimeout !== 'function')return Promise.resolve(timer._onTimeout, args[0]);if (start === undefined && timer._repeat)start = TimerWrap.now();if (!args)timer._onTimeout();elseReflect.apply(timer._onTimeout, timer, args);if (timer._repeat)rearm(timer, start); }

上面的方法執(zhí)行javascript timer._onTimeout();這里是真正的回調(diào)執(zhí)行。

至此,一個(gè)普通的setTimeout方法執(zhí)行完畢。

最后總結(jié)一下調(diào)用流程圖:

總結(jié)

以上是生活随笔為你收集整理的深入解析Node.js setTimeout方法的执行过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。