[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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...