亚洲乱色熟女一区二区三区丝袜,天堂√中文最新版在线,亚洲精品乱码久久久久久蜜桃图片,香蕉久久久久久av成人,欧美丰满熟妇bbb久久久

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

如何優(yōu)雅地實(shí)現(xiàn)每 5 秒輪詢請(qǐng)求?

freeflydom
2025年8月5日 9:53 本文熱度 1282

在做實(shí)時(shí)監(jiān)控系統(tǒng)時(shí),比如服務(wù)器狀態(tài)面板、訂單處理中心或物聯(lián)網(wǎng)設(shè)備看板,每隔 5 秒自動(dòng)拉取最新數(shù)據(jù)是再常見(jiàn)不過(guò)的需求了。

但你有沒(méi)有遇到過(guò)這些問(wèn)題?

  • 頁(yè)面切到后臺(tái)還在瘋狂發(fā)請(qǐng)求,浪費(fèi)資源
  • 上一次請(qǐng)求還沒(méi)回來(lái),下一次又發(fā)了,接口雪崩
  • 用戶切換標(biāo)簽頁(yè)回來(lái),發(fā)現(xiàn)數(shù)據(jù)“卡”在舊狀態(tài)
  • 頁(yè)面銷毀了定時(shí)器還在跑,內(nèi)存泄漏

今天我就以一個(gè)運(yùn)維監(jiān)控平臺(tái)的真實(shí)場(chǎng)景為例,帶你從“能用”做到“好用”。


一、問(wèn)題場(chǎng)景:設(shè)備在線狀態(tài)輪詢

假設(shè)我們要做一個(gè) IDC 機(jī)房設(shè)備監(jiān)控頁(yè),需求如下:

  • 每 5 秒查詢一次所有服務(wù)器的在線狀態(tài)
  • 接口 /api/servers/status 響應(yīng)較慢(平均 1.2s)
  • 用戶可能切換到其他標(biāo)簽頁(yè)處理郵件
  • 頁(yè)面關(guān)閉時(shí)必須停止輪詢

如果直接寫個(gè) setInterval,很容易踩坑。我們一步步來(lái)優(yōu)化。


二、第一版:基礎(chǔ)輪詢(能跑,但有隱患)

import { ref, onMounted, onUnmounted } from 'vue'
const servers = ref([])
let timer = null
onMounted(() => {
  const poll = () => {
    fetch('/api/servers/status')
      .then(res => res.json())
      .then(data => {
        servers.value = data
      })
  }
  poll() // 首次立即執(zhí)行
  timer = setInterval(poll, 5000) // 每5秒輪詢
})
onUnmounted(() => {
  clearInterval(timer) // ?? 清理定時(shí)器
})

? 實(shí)現(xiàn)了基本功能
? 但存在三個(gè)致命問(wèn)題:

  1. 接口未完成就發(fā)起下一次請(qǐng)求 → 可能雪崩
  2. 頁(yè)面不可見(jiàn)時(shí)仍在輪詢 → 浪費(fèi)帶寬和電量
  3. 異常未處理 → 網(wǎng)絡(luò)錯(cuò)誤可能導(dǎo)致后續(xù)不再輪詢

三、第二版:可控輪詢 + 可見(jiàn)性優(yōu)化

我們改用“請(qǐng)求完成后再延遲 5 秒”的策略,避免并發(fā):

import { ref, onMounted, onUnmounted } from 'vue'
const servers = ref([])
let abortController = null // 用于取消請(qǐng)求
const poll = async () => {
  try {
    // 支持取消上一次請(qǐng)求
    abortController?.abort()
    abortController = new AbortController()
    const res = await fetch('/api/servers/status', {
      signal: abortController.signal
    })
    if (!res.ok) throw new Error('Network error')
    
    const data = await res.json()
    servers.value = data
  } catch (err) {
    if (err.name !== 'AbortError') {
      console.warn('輪詢失敗,將重試...', err)
    }
  } finally {
    // ?? 請(qǐng)求結(jié)束后再等5秒發(fā)起下一次
    setTimeout(poll, 5000)
  }
}
onMounted(() => {
  poll() // 啟動(dòng)輪詢
})
onUnmounted(() => {
  abortController?.abort()
})

?? 關(guān)鍵點(diǎn)解析:

  • finally 中 setTimeout 實(shí)現(xiàn)“串行輪詢”,避免并發(fā)
  • AbortController 可在組件卸載時(shí)主動(dòng)取消進(jìn)行中的請(qǐng)求
  • 錯(cuò)誤被捕獲后仍繼續(xù)輪詢,保證穩(wěn)定性

四、第三版:智能節(jié)流 —— 頁(yè)面可見(jiàn)性控制

現(xiàn)在解決“頁(yè)面不可見(jiàn)時(shí)是否輪詢”的問(wèn)題。我們引入 visibilitychange 事件:

let isVisible = true
const handleVisibilityChange = () => {
  isVisible = !document.hidden
  console.log('頁(yè)面可見(jiàn)性:', isVisible ? '可見(jiàn)' : '隱藏')
}
onMounted(() => {
  // 監(jiān)聽(tīng)頁(yè)面可見(jiàn)性
  document.addEventListener('visibilitychange', handleVisibilityChange)
  const poll = async () => {
    try {
      abortController?.abort()
      abortController = new AbortController()
      const res = await fetch('/api/servers/status', {
        signal: abortController.signal
      })
      const data = await res.json()
      servers.value = data
    } catch (err) {
      if (err.name !== 'AbortError') {
        console.warn('輪詢失敗:', err)
      }
    } finally {
      // ?? 只有頁(yè)面可見(jiàn)時(shí)才繼續(xù)輪詢
      if (isVisible) {
        setTimeout(poll, 5000)
      } else {
        // 頁(yè)面隱藏,等待恢復(fù)后再請(qǐng)求
        document.addEventListener('visibilitychange', function waitVisible() {
          if (!document.hidden) {
            document.removeEventListener('visibilitychange', waitVisible)
            setTimeout(poll, 1000) // 恢復(fù)后1秒再查
          }
        }, { once: true })
      }
    }
  }
  poll()
})

?? 這里做了兩層控制:

  1. 頁(yè)面隱藏時(shí),不再自動(dòng)發(fā)起下一輪請(qǐng)求
  2. 頁(yè)面重新可見(jiàn)時(shí),延遲 1 秒觸發(fā)一次查詢,避免瞬間喚醒過(guò)多資源

五、封裝成可復(fù)用的輪詢 Hook

把這套邏輯抽象成通用 usePolling Hook:

// composables/usePolling.js
import { ref } from 'vue'
export function usePolling(fetchFn, interval = 5000) {
  const data = ref(null)
  const loading = ref(false)
  const error = ref(null)
  let abortController = null
  let isVisible = true
  const poll = async () => {
    if (loading.value) return // 防止重復(fù)執(zhí)行
    loading.value = true
    error.value = null
    try {
      abortController?.abort()
      abortController = new AbortController()
      const result = await fetchFn(abortController.signal)
      data.value = result
    } catch (err) {
      if (err.name !== 'AbortError') {
        error.value = err
        console.warn('Polling error:', err)
      }
    } finally {
      loading.value = false
      // ?? 根據(jù)可見(jiàn)性決定是否繼續(xù)
      if (isVisible) {
        setTimeout(poll, interval)
      }
    }
  }
  const start = () => {
    // 移除舊監(jiān)聽(tīng)避免重復(fù)
    document.removeEventListener('visibilitychange', handleVisibility)
    document.addEventListener('visibilitychange', handleVisibility)
    poll()
  }
  const stop = () => {
    abortController?.abort()
    document.removeEventListener('visibilitychange', handleVisibility)
  }
  const handleVisibility = () => {
    isVisible = !document.hidden
    if (isVisible) {
      setTimeout(poll, 1000)
    }
  }
  return { data, loading, error, start, stop }
}

使用方式極其簡(jiǎn)潔:

<script setup>
import { usePolling } from '@/composables/usePolling'
const fetchStatus = async (signal) => {
  const res = await fetch('/api/servers/status', { signal })
  return res.json()
}
const { data, loading } = usePolling(fetchStatus, 5000)
// 自動(dòng)在 onMounted 啟動(dòng)
</script>
<template>
  <div v-if="loading">加載中...</div>
  <ul v-else>
    <li v-for="server in data" :key="server.id">
      {{ server.name }} - {{ server.status }}
    </li>
  </ul>
</template>

六、對(duì)比主流輪詢方案

方案實(shí)現(xiàn)方式優(yōu)點(diǎn)缺點(diǎn)適用場(chǎng)景
setInterval固定間隔觸發(fā)簡(jiǎn)單直觀不考慮響應(yīng)時(shí)間,易并發(fā)快速原型
串行 setTimeout請(qǐng)求完再延時(shí)避免并發(fā),穩(wěn)定周期不嚴(yán)格多數(shù)業(yè)務(wù)場(chǎng)景 ?
WebSocket服務(wù)端推送實(shí)時(shí)性最高成本高,兼容性差股票行情、聊天
Server-Sent Events單向流式推送輕量級(jí)實(shí)時(shí)不支持 IE日志流、通知
智能輪詢(本方案)可見(jiàn)性+串行控制節(jié)能、穩(wěn)定、用戶體驗(yàn)好略復(fù)雜生產(chǎn)環(huán)境推薦 ?


七、舉一反三:三個(gè)變體場(chǎng)景實(shí)現(xiàn)思路

  1. 動(dòng)態(tài)輪詢頻率
    如網(wǎng)絡(luò)異常時(shí)降頻至 30s 一次,正常后恢復(fù) 5s??稍?nbsp;finally 中根據(jù) error.value 動(dòng)態(tài)調(diào)整 setTimeout 時(shí)間。

  2. 多接口協(xié)同輪詢
    多個(gè) API 輪詢但希望錯(cuò)峰發(fā)送??捎?nbsp;Promise.all 組合請(qǐng)求,在 finally 統(tǒng)一控制下一輪時(shí)機(jī),避免瞬間并發(fā)。

  3. 離線重連機(jī)制
    當(dāng)檢測(cè)到網(wǎng)絡(luò)斷開(kāi)(fetch 超時(shí)),改為指數(shù)退避重試(1s → 2s → 4s → 8s),恢復(fù)后再切回 5s 正常輪詢。


小結(jié)

實(shí)現(xiàn)“每 5 秒輪詢”看似簡(jiǎn)單,但要做到穩(wěn)定、節(jié)能、用戶體驗(yàn)好,需要考慮:

  • ? 使用 串行 setTimeout 替代 setInterval,避免請(qǐng)求堆積
  • ? 利用 AbortController 主動(dòng)取消無(wú)用請(qǐng)求
  • ? 結(jié)合 頁(yè)面可見(jiàn)性 API 節(jié)省資源
  • ? 封裝為 可復(fù)用 Hook,提升工程化水平

記住一句話:好的輪詢,是“聰明地少做事”,而不是“拼命做事情”。

下次當(dāng)你接到“每隔 X 秒刷新”的需求時(shí),別急著寫 setInterval,先問(wèn)問(wèn)自己:用戶真的需要這么頻繁嗎?能不能用 WebSocket?頁(yè)面看不見(jiàn)的時(shí)候還要刷嗎?

?轉(zhuǎn)自https://juejin.cn/post/7530948113120624675


該文章在 2025/8/5 9:53:36 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved