復雜計算任務的智能輪詢優化實戰

目錄

復雜計算任務的智能輪詢優化實戰

一、輪詢方法介紹

二、三種輪詢優化策略

1、用 setTimeout 替代 setInterval

2、輪詢時間指數退避

3、標簽頁可見性檢測(Page Visibility API)

三、封裝一個簡單易用的智能輪詢方法

四、結語


????????作者:watermelo37

? ? ? ? CSDN全棧領域優質創作者、萬粉博主、華為云云享專家、阿里云專家博主、騰訊云“創作之星”特邀作者、支付寶合作作者,全平臺博客昵稱watermelo37。

? ? ? ? 一個假裝是giser的coder,做不只專注于業務邏輯的前端工程師,Java、Docker、Python、LLM均有涉獵。

---------------------------------------------------------------------

溫柔地對待溫柔的人,包容的三觀就是最大的溫柔。

---------------------------------------------------------------------

復雜計算任務的智能輪詢優化實戰

一、輪詢方法介紹

????????在前端開發中,我們經常需要輪詢后端任務狀態,例如文件處理、報告生成、復雜計算等長時間任務。如果盲目使用 setInterval,不僅容易浪費資源,還可能造成性能問題。本文將分享一種結合三種策略的優化方案,顯著降低輪詢次數,同時保證用戶體驗。

? ? ? ? 之前寫過一個項目,有非常龐大的數據計算需求,經常會出現一個計算項目需要十幾分鐘,甚至幾十分鐘的情況(但是有緩存的時候又只需要幾十秒),這個時候還是用傳統的 setInterval 輪詢策略顯得太過浪費,占用大量系統資源的同時,也給服務器帶來的一定的負荷,實在是過于笨重。

? ? ? ? 您可以根據您的項目實際需求,選取若干種或者全部策略應用到您的項目中。請注意,只有在長時間復雜計算任務的輪詢中,才會有極大的優化效果,短時間(幾秒或者十幾秒)的計算任務輪詢本身就不占用太多資源,優化效果不佳。

? ? ? ? 最重要的是面對甲方,這種優化非常有牌面,匯報的時候可以做做文章。

二、三種輪詢優化策略

1、用 setTimeout 替代 setInterval

????????傳統輪詢做法是這樣的:

const intervalId = setInterval(async () => {const result = await checkTaskStatus();if (result.done) clearInterval(intervalId);
}, 2000);

????????如果網絡波動或者請求延遲,可能出現多次輪詢重疊,造成額外請求和壓力。而且這種基于本地時間的尋輪本身就是不符合實際情況的。

????????優化做法:遞歸調用 setTimeout:

async function pollTask() {const result = await checkTaskStatus();if (!result.done) {setTimeout(pollTask, 2000);}
}pollTask();

? ? ? ? 這樣可以避免請求重疊,網絡慢時自然延后下一次輪詢,而且無需手動清除定時器,更加靈活。

2、輪詢時間指數退避

????????對于長時間任務,前幾次輪詢未返回結果,就意味著任務耗時較長,盲目頻繁輪詢沒必要。指數退避可以動態增加輪詢間隔,降低冗余請求:

let attempt = 0;
const baseInterval = 1000; // 初始間隔 1s
const maxInterval = 30000; // 最大間隔 30sasync function pollWithBackoff() {const result = await checkTaskStatus();if (!result.done) {attempt++;const interval = Math.min(baseInterval * 1.5 ** attempt, maxInterval);setTimeout(pollWithBackoff, interval);}
}pollWithBackoff();

? ? ? ? 需要根據歷史任務完成時間調整指數底數或最大間隔。

? ? ? ? 這樣的話前幾次快速輪詢,保證非長時間計算任務(比如計算量小或者有緩存記錄)用戶能盡早獲取結果。隨著嘗試次數增加,輪詢間隔指數增長,顯著降低冗余請求。

3、標簽頁可見性檢測(Page Visibility API)

????????在長時間任務中,用戶經常會選擇將頁面放到后臺,這個時候可以通過 Page Visibility API 進一步調整輪詢策略,降低不必要的CPU和網絡資源占用。將輪詢的時長進一步延長到一個極大值。如果用戶將標簽頁放到前臺,說明他想檢查計算結果。那么次數如果間隔時間超過了指數退避應有的時間,但是沒到后臺間隔時間,會立即觸發輪詢,檢查狀態。

let isPolling = false;
let timeoutId = null;async function smartPoll() {if (isPolling) return; // 已經有輪詢在進行,不再觸發isPolling = true;const result = await checkTaskStatus();isPolling = false;if (!result.done) {let nextInterval;if (!isPageVisible) {nextInterval = 60000; // 后臺最大延遲} else {nextInterval = Math.min(baseInterval * 2 ** attempt, maxInterval);}timeoutId = setTimeout(smartPoll, nextInterval);}
}// 用戶回到前臺時立即觸發
document.addEventListener("visibilitychange", () => {isPageVisible = !document.hidden;if (isPageVisible) {// 取消原來的定時器,立即輪詢if (timeoutId) clearTimeout(timeoutId);smartPoll();}
});smartPoll();

? ? ? ? 這樣可以在用戶不關注時,降低輪詢頻率,節省資源,用戶切回前臺時,立即按照指數退避策略或更短間隔繼續輪詢,保證響應及時。

三、封裝一個簡單易用的智能輪詢方法

? ? ? ? 上述三種方式疊加起來,就可以封裝成一個非常優秀的長時間復雜計算任務輪詢的優化算法,具有如下優勢:

  • 自適應退避策略

????????前臺輪詢:采用指數退避(baseInterval * 2^attempt),避免短時間內重復請求。

????????后臺輪詢:固定間隔 maxBackoff,節省 CPU 和網絡資源。

  • 抖動機制(Jitter)

????????在計算間隔時加入隨機抖動(±jitterRatio),避免大量客戶端同時請求導致“雪崩效應”。

  • 可見性感知

????????頁面切換到后臺時自動延長輪詢間隔,頁面切回前臺時會立即觸發一次搶跑,保證用戶看到最新數據。

  • 錯誤處理與自動重試

????????異步任務出錯時,前臺依然采用指數退避,后臺采用固定間隔重試,任務出錯不會中斷整個輪詢流程。

  • 任務取消支持

????????每輪任務都會傳入新的 AbortSignal,調用 poller.stop() 或新任務啟動時可中止上一輪任務。

  • 服務端可控間隔

????????如果任務返回 retryAfter,會優先采用服務器建議的輪詢間隔,同時重置指數退避。

  • 封裝完善,簡潔易用

????????啟動輪詢只需調用 createSmartPoll 并傳入異步任務即可,提供 stop 方法一鍵停止輪詢及清理資源。

????????在實際開發中效果非常好:

/*** 智能輪詢* @param {(signal: AbortSignal) => Promise<{ done: boolean, retryAfter?: number }>} taskFn* @param {Object} options*   baseInterval: 初始輪詢間隔(ms),默認 1000*   maxInterval: 最大輪詢間隔(ms),默認 30000*   maxBackoff: 頁面隱藏時的固定間隔(ms),默認 60000*   jitter: 抖動系數(0~1),默認 0.2 表示 ±20%*/
function createSmartPoll(taskFn, options = {}) {const baseInterval = options.baseInterval ?? 1000;const maxInterval  = options.maxInterval  ?? 30000;const maxBackoff   = options.maxBackoff   ?? 60000;const jitterRatio  = options.jitter ?? 0.2;let attemptVisible = 0;      // 僅在「可見 + 未完成/出錯」時增長let isPageVisible  = typeof document !== 'undefined' ? !document.hidden : true;let timeoutId      = null;let isPolling      = false;let stopped        = false;let controller     = new AbortController();const addJitter = (ms) => {if (!jitterRatio) return ms;const delta = ms * jitterRatio;return Math.max(0, ms + (Math.random() * 2 - 1) * delta);};const cleanup = () => {if (timeoutId) {clearTimeout(timeoutId);timeoutId = null;}if (typeof document !== 'undefined') {document.removeEventListener('visibilitychange', onVisibility);}controller.abort();stopped = true;};async function poll() {if (stopped || isPolling) return;isPolling = true;try {// 確保每輪都有自己的 abort 信號controller.abort(); // 取消上一輪遺留controller = new AbortController();const result = await taskFn(controller.signal);const done = !!(result && typeof result.done === 'boolean' ? result.done : false);if (done) {cleanup();return;}// 計算下一次間隔let interval;if (!isPageVisible) {// 后臺固定間隔,并且不增長 attemptVisibleinterval = maxBackoff;} else if (result && typeof result.retryAfter === 'number') {// 服務器/任務建議的間隔優先interval = Math.max(0, Math.min(result.retryAfter, maxInterval));attemptVisible = 0; // 有明確指示時可視為“重置退避”} else {attemptVisible++;interval = Math.min(baseInterval * (1.5 ** attemptVisible), maxInterval);}interval = addJitter(interval);// 先清舊的,避免遺留定時器if (timeoutId) clearTimeout(timeoutId);timeoutId = setTimeout(() => {timeoutId = null;poll();}, interval);} catch (err) {console.error('輪詢任務出錯:', err);// 出錯:可見時才增長退避;后臺固定間隔let interval;if (!isPageVisible) {interval = maxBackoff;} else {attemptVisible++;interval = Math.min(baseInterval * (2 ** attemptVisible), maxInterval);}interval = addJitter(interval);if (timeoutId) clearTimeout(timeoutId);timeoutId = setTimeout(() => {timeoutId = null;poll();}, interval);} finally {isPolling = false;}}function onVisibility() {const nowVisible = !document.hidden;if (nowVisible === isPageVisible) return;isPageVisible = nowVisible;if (isPageVisible) {// 回到前臺:無條件搶跑一次if (timeoutId) {clearTimeout(timeoutId);timeoutId = null;}// 若當前正在執行,等其結束;立即排一個 0ms 的下一輪timeoutId = setTimeout(() => {timeoutId = null;poll();}, 0);}}if (typeof document !== 'undefined') {document.addEventListener('visibilitychange', onVisibility);}// 啟動poll();return {stop: cleanup};
}

????????調用案例如下:

// 模擬異步任務
async function fetchData(signal) {// 這里用 fetch 舉例,可傳 signal 用于取消try {const response = await fetch('/api/status', { signal });const data = await response.json();// 假設服務器返回 { finished: boolean, nextCheck: number(ms) }return {done: data.finished,retryAfter: data.nextCheck // 可選};} catch (err) {if (err.name === 'AbortError') {console.log('任務被中止');} else {console.warn('請求錯誤:', err);}// 出錯返回 done=false 表示繼續輪詢return { done: false };}
}// 創建智能輪詢實例
const poller = createSmartPoll(fetchData, {baseInterval: 1000,   // 初始 1 秒maxInterval: 10000,   // 最大 10 秒maxBackoff: 30000,    // 后臺固定 30 秒jitter: 0.2           // ±20% 抖動
});// 停止輪詢示例
setTimeout(() => {poller.stop();console.log('輪詢已停止');
}, 60000); // 1 分鐘后停止

四、結語

通過以下三種策略的疊加,可以極大優化長時間任務輪詢:

  1. 遞歸 setTimeout:避免輪詢疊加,更靈活。

  2. 指數退避:減少長任務中無效輪詢。

  3. 標簽頁可見性檢測:降低后臺頁面的資源消耗。

????????實踐中,這三種策略結合起來,既保證了用戶體驗,又大幅降低了服務器壓力。未來可結合任務分布數據和智能算法進一步優化輪詢策略,實現真正的“智能輪詢”。

????????只有鍛煉思維才能可持續地解決問題,只有思維才是真正值得學習和分享的核心要素。如果這篇博客能給您帶來一點幫助,麻煩您點個贊支持一下,還可以收藏起來以備不時之需,有疑問和錯誤歡迎在評論區指出~

????????其他熱門文章,請關注:

? ? ? ??極致的靈活度滿足工程美學:用Vue Flow繪制一個完美流程圖

???? ? ?你真的會使用Vue3的onMounted鉤子函數嗎?Vue3中onMounted的用法詳解

?? ? ? ?Web Worker:讓前端飛起來的隱形引擎

????????DeepSeek:全棧開發者視角下的AI革命者

??? ? ??通過array.filter()實現數組的數據篩選、數據清洗和鏈式調用

?? ? ? ?測評:這B班上的值不值?在不同城市過上同等生活水平到底需要多少錢?

??? ? ??通過Array.sort() 實現多字段排序、排序穩定性、隨機排序洗牌算法、優化排序性能

??? ? ??TreeSize:免費的磁盤清理與管理神器,解決C盤爆滿的燃眉之急

??? ? ??通過MongoDB Atlas 實現語義搜索與 RAG——邁向AI的搜索機制

???? ? ?深入理解 JavaScript 中的 Array.find() 方法:原理、性能優勢與實用案例詳解

??? ? ??前端實戰:基于Vue3與免費滿血版DeepSeek實現無限滾動+懶加載+瀑布流模塊及優化策略

???? ? ?el-table實現動態數據的實時排序,一篇文章講清楚elementui的表格排序功能

?? ? ? ?JavaScript雙問號操作符(??)詳解,解決使用 || 時因類型轉換帶來的問題

?? ? ?【前端實戰】如何讓用戶回到上次閱讀的位置?

? ? ? ??內存泄漏——海量數據背后隱藏的項目生產環境崩潰風險!如何避免內存泄漏

??? ? ??MutationObserver詳解+案例——深入理解 JavaScript 中的 MutationObserver

????? ??高效工作流:用Mermaid繪制你的專屬流程圖;如何在Vue3中導入mermaid繪制流程圖

????????JavaScript中通過array.map()實現數據轉換、創建派生數組、異步數據流處理、DOM操作等

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98400.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98400.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98400.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java開發中常用CollectionUtils方式,以及Spring中CollectionUtils常用方法示例

場景 Java開發中常用的CollectionUtils 一、Spring Framework的CollectionUtils 包路徑&#xff1a;org.springframework.util.CollectionUtils 核心方法&#xff1a; isEmpty(Collection<?> coll) List<String> list null; boolean empty CollectionUtil…

人工智能學習:Transformer結構(文本嵌入及其位置編碼器)

一、輸入部分介紹 輸入部分包含: 編碼器源文本嵌入層及其位置編碼器 解碼器目標文本嵌入層及其位置編碼器 在transformer的encoder和decoder的輸入層中,使用了Positional Encoding,使得最終的輸入滿足: 這里,input_embedding是通過常規embedding層,將每一個詞的…

? 肆 ? ? 默認安全建設方案:c-1.增量風險管控

&#x1f44d;點「贊」&#x1f4cc;收「藏」&#x1f440;關「注」&#x1f4ac;評「論」 在金融科技深度融合的背景下&#xff0c;信息安全已從單純的技術攻防擴展至架構、合規、流程與創新的系統工程。作為一名從業十多年的老兵&#xff0c;將系統闡述數字銀行安全體系的建設…

第二課、熟悉Cocos Creator 編輯器界面

本文主要介紹Cocos Creator 編輯器界面中幾個常規的面板功能&#xff0c;讓新手了解編輯器界面中常規的面板功能&#xff0c;更好的使用Cocos Creator 編輯器。一、編輯器界面常規面板劃分Cocos Creater編輯器默認樣式如上&#xff0c;主要包含&#xff1a;1、工具欄&#xff0…

Elixir通過Onvif協議控制IP攝像機,擴展ExOnvif的攝像頭連續移動功能 ContinuousMove

Elixir 通過Onvif 對IP設備進行控制時&#xff0c;可以使用 ExOnvif 庫。ExOnvif官方文檔 此文章僅提供了ContinuousMove的控制方式及示例。 Elixir Onvif協議控制IP設備的其他命令&#xff0c;可以參考以下鏈接 絕對移動 【AbsoluteMove】 調用指定預置位 【GotoPreset】 …

android studio JNI 環境配置實現 java 調用 c/c++

1、在 app 級的 build.gradle 文件配置兩個地方 android{ defaultConfig{ // 在 defaultConfig 里配置下面代碼 externalNativeBuild { cmake { cppFlags "-frtti -fexceptions"//添加對 c 的異常處理支持 …

靜態時序分析詳解之時序路徑類型

目錄 一、概覽 二、時序路徑 2.1 數據路徑 2.2 時鐘路徑 2.3 時鐘門控路徑 2.4 異步路徑 2.5 關鍵路徑 2.6 False路徑 2.7 單周期路徑 2.8 多周期路徑 2.9 最長路徑和最短路徑 三、參考資料 一、概覽 ? ?靜態時序分析通過模擬最差條件下分析所有的時序路徑&am…

SpringBoot埋點功能技術實現方案深度解析:架構設計、性能優化與擴展性實踐

SpringBoot埋點功能技術實現方案深度解析&#xff1a;架構設計、性能優化與擴展性實踐 1. 原理剖析與技術實現細節 1.1 埋點技術基本原理 埋點&#xff08;Tracking&#xff09;是通過在代碼中植入特定邏輯&#xff0c;收集用戶行為數據、系統運行狀態和業務指標的技術手段。在…

自建prometheus監控騰訊云k8s集群

自建prometheus監控騰訊云k8s集群 使用場景 k8s集群&#xff08;騰訊云容器服務&#xff09; promtheus (外部自建服務) 騰訊云提供了容器內部自建 Prometheus 監控 TKE 集群的文檔&#xff0c;參考。 當前的環境promethues建在k8S外的云服務器上&#xff0c;與上面鏈接文…

2025高教社國賽數學建模C題參考論文(含模型和代碼)

2025 年高教社杯大學生數學建模競賽 C 題參考論文 目錄 NIPT 的時點選擇與胎兒的異常判定 摘要 1 問題重述 2 問題分析 2.1 問題 1 分析 2.2 問題 2 分析 2.3 問題 3 分析 2.4 問題 4 分析 3 模型假設與符號定義 3.1 模型假設 4. 孕周在 10-25 周內檢測有…

iOS開發環境搭建及打包流程

一、下載xcode 直接去蘋果商店的appstore下載就行 二、clone項目 1.登錄xcode蘋果賬號或對應代碼倉庫賬號 2.clone項目 3.安裝設備真機環境&#xff08;未安裝過的話&#xff09; 三.安裝cocoapods 1. 檢查并更新 Ruby 環境 CocoaPods 是基于 Ruby 編寫的&#xff0c;因此…

數據結構之鏈表(單向鏈表與雙向鏈表)

一&#xff0c;鏈表描述鏈表是一種常見的重要的數據結構,是動態地進行存儲分配的一種結構。常用于需存儲的數據的數目無法事先確定。1.鏈表的一般結構鏈表的組成&#xff1a; 頭指針&#xff1a;存放一個地址&#xff0c;該地址指向一個元素 結點&#xff1a;用戶需要的實際數據…

從反向代理到負載均衡:Nginx + Tomcat 構建高可用Web服務架構

從反向代理到負載均衡&#xff1a;Nginx Tomcat 構建高可用Web服務架構 文章目錄從反向代理到負載均衡&#xff1a;Nginx Tomcat 構建高可用Web服務架構一、基礎鋪墊&#xff1a;什么是反向代理&#xff1f;1.1 反向代理的核心原理1.2 Nginx反向代理實戰配置步驟1&#xff1a…

Simulink中使用Test sequence單元測試

一、Tips 在對simulink模型進行Test sequence單元測試時&#xff0c;如果采取書寫測試用例的話&#xff0c;有以下操作。 1、使用”fprintf(‘time%f\n’, t);“來打印當前step的時間&#xff1b; 二、數據類型轉換 1、double類型 -> boolean類型 clc; clear all;% 1、doubl…

【mysql】SQL自連接:什么時候需要,什么時候不需要?

SQL自連接:什么時候需要,什么時候不需要? 通過具體示例和對比解析,徹底搞懂SQL自連接的使用場景 在處理SQL查詢時,尤其是當表中存在自引用關系(如referee_id引用同一張表的id)時,很多開發者會疑惑:這個查詢到底需不需要自連接?本文將通過多個具體示例,帶你徹底弄清何…

「美」創新在于人,而不是產品 - AxureMost 落葵網

添加圖片注釋&#xff0c;不超過 140 字&#xff08;可選&#xff09; 第一章&#xff1a;創新的心理學 創新與心理安全 蠟燭問題&#xff1a;卡爾鄧克爾的蠟燭問題實驗揭示了創造性思維的重要性。通過顛覆對盒子用途的先入為主觀念&#xff0c;參與者能夠找到創新性的解決方案…

新規則,新游戲:AI時代下的戰略重構與商業實踐

當你的客服AI能夠真正像員工一樣理解客戶的行業術語&#xff0c;當AI能主動從大量的客戶咨詢中篩選出高價值潛在客戶 —— 這已經不再是理想中才能存在的場景&#xff0c;而是當下 “人工智能 ” 行動深入推進中&#xff0c;企業智能化轉型的真實寫照。 "人工智能 " …

ScanNet: Richly-annotated 3D Reconstructions of Indoor Scenes 數據集構建

paper link: paperlink Abstract: 這個數據集是個RGB-D視頻數據集&#xff0c;在707個不同空間中獲取了1513個掃描的場景&#xff0c;250w個視圖&#xff0c;并且標注了相機位姿&#xff0c;表面重建&#xff0c;語義分割。本數據集共有20人掃描500名工作者進行標注。 數據集…

c語言期末復習

一、選擇題(10道) 1、以下哪個不是C語言的關鍵字? A) int B) float C) string D) while (答案:C) 2、表達式 5 / 2 的結果是: A) 2.5 B) 2 C) 3 D) 2.0 (答案:B) 3、指針變量存儲的是: A) 變量的值 B) 變量的地址 C) 變量的類型 D) 變量的名稱 (答案:B) 4、以…

JLINK 調試器單步調試單片機

0 JLINK 調試器單步調試單片機 1 物理層1.1 調整電壓和開發板一致2 環境搭建 2.1 安裝 JLink_Windows_V862_x86_642.2 vscode 配置 {"version": "0.2.0","configurations": [{"name": "(gdb) 啟動","type": "…