最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

利用JavaScript優(yōu)雅地實(shí)現(xiàn)每5秒輪詢請(qǐng)求功能

 更新時(shí)間:2025年07月28日 08:37:32   作者:前端微白  
在做實(shí)時(shí)監(jiān)控系統(tǒng)時(shí),比如服務(wù)器狀態(tài)面板、訂單處理中心或物聯(lián)網(wǎng)設(shè)備看板,每隔 5 秒自動(dòng)拉取最新數(shù)據(jù)是再常見不過的需求了,但是會(huì)遇到好多問題今天我就以一個(gè)運(yùn)維監(jiān)控平臺(tái)的真實(shí)場(chǎng)景為例,帶你從能用做到好用,需要的朋友可以參考下

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

但你有沒有遇到過這些問題?

  • 頁(yè)面切到后臺(tái)還在瘋狂發(fā)請(qǐng)求,浪費(fèi)資源
  • 上一次請(qǐng)求還沒回來(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)景為例,帶你從“能用”做到“好用”。

一、問題場(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è)致命問題:

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

三、第二版:可控輪詢 + 可見性優(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)解析:

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

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

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

let isVisible = true

const handleVisibilityChange = () => {
  isVisible = !document.hidden
  console.log('頁(yè)面可見性:', isVisible ? '可見' : '隱藏')
}

onMounted(() => {
  // 監(jiān)聽頁(yè)面可見性
  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è)面可見時(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è)面重新可見時(shí),延遲 1 秒觸發(fā)一次查詢,避免瞬間喚醒過多資源

五、封裝成可復(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ù)可見性決定是否繼續(xù)
      if (isVisible) {
        setTimeout(poll, interval)
      }
    }
  }

  const start = () => {
    // 移除舊監(jiān)聽避免重復(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é)能、穩(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。可在 finally 中根據(jù) error.value 動(dòng)態(tài)調(diào)整 setTimeout 時(shí)間。

  1. 多接口協(xié)同輪詢

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

  1. 離線重連機(jī)制

當(dāng)檢測(cè)到網(wǎng)絡(luò)斷開(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è)面可見性 API 節(jié)省資源
  • 封裝為 可復(fù)用 Hook,提升工程化水平

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

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

以上就是利用JavaScript優(yōu)雅地實(shí)現(xiàn)每5秒輪詢請(qǐng)求功能的詳細(xì)內(nèi)容,更多關(guān)于JavaScript每5秒輪詢請(qǐng)求的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

介休市| 庆阳市| 阳高县| 平湖市| 武邑县| 桃江县| 大厂| 济源市| 绥棱县| 西充县| 宽城| 绥阳县| 通许县| 泰兴市| 高要市| 区。| 巍山| 长岭县| 工布江达县| 星座| 麦盖提县| 库伦旗| 南郑县| 自贡市| 嵊州市| 临武县| 拉萨市| 建昌县| 东安县| 灵丘县| 阳谷县| 哈巴河县| 禄丰县| 桂林市| 额敏县| 资兴市| 合川市| 理塘县| 宁陵县| 比如县| 岳西县|