[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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...