[NodeJS] timers階段的源碼解析

来源:https://www.cnblogs.com/feixianxing/p/18284332/nodejs-event-loop-timers
-Advertisement-
Play Games

本文探討了Node.js事件迴圈中的timers階段,分析了定時器的管理和執行過程。通過源碼解析,揭示了定時器超時檢查、回調執行以及定時器類型(setTimeout與setInterval)的內部實現機制。文章旨在幫助讀者理解Node.js中定時器的工作原理及其在事件驅動編程中的重要性。 ...


timers階段是Nodejs事件迴圈中的一個階段,這一階段主要是檢查是否有到期的定時器,如果有則執行其回調。

相關源碼位置:

timers階段的代碼比較少,這裡直接貼出來,你也可以點進去上面的源碼看自己感興趣的部分:

void uv__run_timers(uv_loop_t* loop) {
  struct heap_node* heap_node;
  uv_timer_t* handle;
  struct uv__queue* queue_node;
  struct uv__queue ready_queue;
  
  // 初始化一個空的 ready_queue 隊列,用於存放已經到期的定時器
  uv__queue_init(&ready_queue);

  for (;;) {
    // 獲取堆中最小(即最早到期)的定時器節點
    heap_node = heap_min(timer_heap(loop));
    if (heap_node == NULL)
      break;  // 如果堆是空的(沒有定時器),則跳出迴圈

    // 將堆節點轉換為 uv_timer_t 類型的定時器句柄 handle
    handle = container_of(heap_node, uv_timer_t, node.heap);
    if (handle->timeout > loop->time)
      break;  // 如果當前定時器的超時時間大於當前的迴圈時間,則跳出迴圈

    // 停止到期的定時器,並將其插入到 ready_queue 隊列的尾部
    uv_timer_stop(handle);
    uv__queue_insert_tail(&ready_queue, &handle->node.queue);
  }
  
  // 處理 ready_queue 中的所有定時器
  while (!uv__queue_empty(&ready_queue)) {
    queue_node = uv__queue_head(&ready_queue);  // 取出隊列頭部的節點
    uv__queue_remove(queue_node);  // 移除該節點
    uv__queue_init(queue_node);  // 重新初始化節點
    handle = container_of(queue_node, uv_timer_t, node.queue);  // 將節點轉換為定時器句柄 handle

    // 重新啟動定時器,如果是重覆定時器,則根據設定的間隔重新計算超時時間
    uv_timer_again(handle);
    // 調用定時器的回調函數,執行定時器到期後的操作
    handle->timer_cb(handle);
  }
}

內容比較少,這裡也直接貼出來:

int uv_timer_again(uv_timer_t* handle) {
  if (handle->timer_cb == NULL)
    return UV_EINVAL;

  if (handle->repeat) {
    uv_timer_stop(handle);
    uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat);
  }

  return 0;
}

可以看到會檢查handle->repeat,這裡其實就是setTimeoutsetInterval的區別了,setInterval到這裡會因為handle->repeattrue而重新開啟新的一輪計時,而setTimeout則是直接跳過、結束了。

這裡的handle->repeatuint64_t類型,其實就是timeout

這裡順便貼一下uv_timer_start的代碼,感興趣可以看看。

源碼位置:node/deps/uv/src/timer.c at main · nodejs/node (github.com)

int uv_timer_start(uv_timer_t* handle,
                   uv_timer_cb cb,
                   uint64_t timeout,
                   uint64_t repeat) {
  uint64_t clamped_timeout;
  
  // 如果定時器正在關閉或回調函數為 NULL,則返回錯誤代碼 UV_EINVAL
  if (uv__is_closing(handle) || cb == NULL)
    return UV_EINVAL;
  
  // 停止定時器,以確保定時器在重新啟動前沒有在運行
  uv_timer_stop(handle);
  
  // 計算定時器的超時時間 clamped_timeout
  // 它是當前事件迴圈時間 handle->loop->time 加上 timeout
  clamped_timeout = handle->loop->time + timeout;
  // 如果發生整數溢出,導致 clamped_timeout 小於 timeout,
  // 則將 clamped_timeout 設置為最大值 UINT64_MAX
  if (clamped_timeout < timeout)
    clamped_timeout = (uint64_t) -1;

  // 設置定時器的回調函數、超時時間和重覆間隔
  handle->timer_cb = cb;
  handle->timeout = clamped_timeout;
  handle->repeat = repeat;
  
  // start_id 用於在定時器比較函數 timer_less_than 中作為第二索引進行比較
  // 並遞增事件迴圈的計時器計數器
  handle->start_id = handle->loop->timer_counter++;

  // 將定時器插入到事件迴圈的定時器堆中,並啟動定時器句柄
  heap_insert(timer_heap(handle->loop),
              (struct heap_node*) &handle->node.heap,
              timer_less_than);
  uv__handle_start(handle);

  return 0;
}
  • clamped_timeouttimeout之間的關係:

    • timeout 是傳遞給 uv_timer_start 函數的參數,表示定時器的初始延遲時間,以毫秒為單位;

    • clamped_timeout 是計算後的絕對時間,表示定時器實際超時的絕對時間點(以事件迴圈的時間 handle->loop->time 為基準)。

  • uv_timer_starttimeoute參數 和 JS中setTimeoutdelay參數是等價的。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • zustand 和 jotai 是當下比較流行的react狀態管理庫。其都有著輕量、方便使用,和react hooks能夠很好的搭配,並且性能方面,對比React自身提供的context要好得多,因此被很多開發小伙伴所喜愛。 更有意思的是,這兩個庫的作者是同一個人,同時他還開源了另外一個狀態庫 va ...
  • 前言 vue2的時候想必大家有遇到需要在style模塊中訪問script模塊中的響應式變數,為此我們不得不使用css變數去實現。現在vue3已經內置了這個功能啦,可以在style中使用v-bind指令綁定script模塊中的響應式變數,這篇文章我們來講講vue是如何實現在style中使用script ...
  • title: Nuxt框架中內置組件詳解及使用指南(二) date: 2024/7/7 updated: 2024/7/7 author: cmdragon excerpt: 摘要:“本文詳細介紹了Nuxt 3中和組件的使用方法,包括組件的基本概念、屬性、自定義屬性、獲取引用以及完整示例,展示瞭如何 ...
  • title: Nuxt框架中內置組件詳解及使用指南(一) date: 2024/7/6 updated: 2024/7/6 author: cmdragon excerpt: 本文詳細介紹了Nuxt框架中的兩個內置組件和的使用方法與功能。確保包裹的內容僅在客戶端渲染,適用於處理瀏覽器特定功能或非同步數 ...
  • 本文探討了JavaScript中Promise的基礎用法和各種靜態方法的應用場景。從解決非同步編程中的回調地獄問題,到鏈式調用、併發請求控制,再到最新的Promise.allSettled和Promise.any的應用。每種方法均通過代碼示例和詳細的應用場景進行了展示。 ...
  • 摘要:本文詳細介紹了Nuxt3中幾個關鍵的生命周期鉤子和它們的使用方法,包括webpack:done用於Webpack編譯完成後執行操作,webpack:progress監聽編譯進度,render:response和render:html分別在響應發送前後修改響應內容,以及render:island... ...
  • ‍ 寫在開頭 點贊 + 收藏 學會 7 種方案解決移動端1px邊框的問題 造成邊框變粗的原因 css中的1px並不等於移動設備的1px,這是由不同手機由不同像素密度,在window對象中有一個devicePixelRatio屬性,他可以反應css中像素與設備的像素比 device ...
  • ## 動態表單系統:利用 Spring Boot 和 MyBatis 實現後端服務 在現代企業應用中,表單是數據收集和處理的核心部分。然而,傳統的表單系統難以適應快速變化的需求。為瞭解決這個問題,我們可以使用動態表單系統,它可以根據業務需求靈活地調整表單結構。本文將介紹如何使用 Spring Boo ...
一周排行
    -Advertisement-
    Play Games
  • 通過WPF的按鈕、文本輸入框實現了一個簡單的SpinBox數字輸入用戶組件並可以通過數據綁定數值和步長。本文中介紹了通過Xaml代碼實現自定義組件的佈局,依賴屬性的定義和使用等知識點。 ...
  • 以前,我看到一個朋友在對一個系統做初始化的時候,通過一組魔幻般的按鍵,調出來一個隱藏的系統設置界面,這個界面在常規的菜單或者工具欄是看不到的,因為它是一個後臺設置的關鍵界面,不公開,同時避免常規用戶的誤操作,它是作為一個超級管理員的入口功能,這個是很不錯的思路。其實Winform做這樣的處理也是很容... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他的程式每次關閉時就會自動崩潰,一直找不到原因讓我幫忙看一下怎麼回事,這位朋友應該是第二次找我了,分析了下 dump 還是挺經典的,拿出來給大家分享一下吧。 二:WinDbg 分析 1. 為什麼會崩潰 找崩潰原因比較簡單,用 !analyze -v 命 ...
  • 在一些報表模塊中,需要我們根據用戶操作的名稱,來動態根據人員姓名,更新報表的簽名圖片,也就是電子手寫簽名效果,本篇隨筆介紹一下使用FastReport報表動態更新人員簽名圖片。 ...
  • 最新內容優先發佈於個人博客:小虎技術分享站,隨後逐步搬運到博客園。 創作不易,如果覺得有用請在Github上為博主點亮一顆小星星吧! 博主開始學習編程於11年前,年少時還只會使用cin 和cout ,給單片機點點燈。那時候,類似async/await 和future/promise 模型的認知還不是 ...
  • 之前在阿裡雲ECS 99元/年的活動實例上搭建了一個測試用的MINIO服務,以前都是直接當基礎設施來使用的,這次準備自己學一下S3相容API相關的對象存儲開發,因此有了這個小工具。目前僅包含上傳功能,後續計劃開發一個類似圖床的對象存儲應用。 ...
  • 目錄簡介快速入門安裝 NuGet 包實體類User資料庫類DbFactory增刪改查InsertSelectUpdateDelete總結 簡介 NPoco 是 PetaPoco 的一個分支,具有一些額外的功能,截至現在 github 星數 839。NPoco 中文資料沒多少,我是被博客園群友推薦的, ...
  • 前言 前面使用 Admin.Core 的代碼生成器生成了通用代碼生成器的基礎模塊 分組,模板,項目,項目模型,項目欄位的基礎功能,本篇繼續完善,實現最核心的模板生成功能,並提供生成預覽及代碼文件壓縮下載 準備 首先清楚幾個模塊的關係,如何使用,簡單畫一個流程圖 前面完成了基礎的模板組,模板管理,項目 ...
  • 假設需要實現一個圖標和文本結合的按鈕 ,普通做法是 直接重寫該按鈕的模板; 如果想作為通用的呢? 兩種做法: 附加屬性 自定義控制項 推薦使用附加屬性的形式 第一種:附加屬性 創建Button的附加屬性 ButtonExtensions 1 public static class ButtonExte ...
  • 在C#中,委托是一種引用類型的數據類型,允許我們封裝方法的引用。通過使用委托,我們可以將方法作為參數傳遞給其他方法,或者將多個方法組合在一起,從而實現更靈活的編程模式。委托類似於函數指針,但提供了類型安全和垃圾回收等現代語言特性。 基本概念 定義委托 定義委托需要指定它所代表的方法的原型,包括返回類 ...