在实际项目中,我们经常会遇到多个接口需要定时刷新的场景,比如:

  • 实时监控面板:多个指标接口定时拉取

  • 后台任务状态:周期检查任务执行情况

  • 消息或通知中心:定时拉取新消息

如果每个接口都单独写一个 setInterval,不仅代码冗余,还容易造成内存泄漏、难以管理、互相干扰等问题。

本文带来一个可控性强、性能优雅的 Vue3 Hook —— useMultiPolling,用于统一管理多个轮询任务。


💡 功能特点

✅ 支持多任务独立启动/停止
✅ 支持异步任务(Promise)
✅ 自动在组件卸载时清理定时器
✅ 可随时动态扩展任务
✅ 支持局部或全局停止


🧩 Hook 实现

import { onUnmounted } from 'vue'

interface PollingTask {
  name: string
  callback: () => void | Promise<void>
  interval: number
}

export function useMultiPolling(tasks: PollingTask[]) {
  const timers = new Map<string, NodeJS.Timeout>()
  const taskMap = new Map<string, PollingTask>()

  // 初始化任务映射
  for (const task of tasks) {
    taskMap.set(task.name, task)
  }

  /** 启动任务(支持单个或全部) */
  const startPolling = (taskName?: string) => {
    const taskList = taskName ? [taskMap.get(taskName)] : tasks

    for (const task of taskList) {
      if (!task || timers.has(task.name)) continue

      const run = async () => {
        try {
          await task.callback()
        } finally {
          const timer = setTimeout(run, task.interval)
          timers.set(task.name, timer)
        }
      }
      run()
    }
  }

  /** 停止任务(支持单个或全部) */
  const stopPolling = (taskName?: string) => {
    if (taskName) {
      const timer = timers.get(taskName)
      if (timer) {
        clearTimeout(timer)
        timers.delete(taskName)
      }
    } else {
      for (const timer of timers.values()) clearTimeout(timer)
      timers.clear()
    }
  }

  /** 组件卸载时自动清理 */
  onUnmounted(stopPolling)

  return { startPolling, stopPolling }
}

🧠 使用示例

<script setup lang="ts">
import { useMultiPolling } from '@/hooks/useMultiPolling'

const { startPolling, stopPolling } = useMultiPolling([
  {
    name: 'userStatus',
    callback: async () => {
      console.log('拉取用户状态...')
      // await api.getUserStatus()
    },
    interval: 5000,
  },
  {
    name: 'taskProgress',
    callback: async () => {
      console.log('检查任务进度...')
      // await api.getTaskProgress()
    },
    interval: 3000,
  },
])

onMounted(() => {
  startPolling() // 启动所有任务
})

onUnmounted(() => {
  stopPolling() // 组件卸载自动清理
})
</script>

⚙️ 进阶用法

1️⃣ 单独控制任务

startPolling('userStatus')  // 仅启动用户状态轮询
stopPolling('taskProgress') // 暂停任务进度轮询

2️⃣ 动态添加任务

你可以在运行时更新任务列表:

tasks.push({
  name: 'newData',
  callback: () => fetch('/api/newData'),
  interval: 8000,
})

🧩 优化点解析

优化点说明
setTimeout + 递归避免 setInterval 累积执行延迟
Promise 支持可安全执行异步请求任务
Map 存储定时器任务查找与清理效率高
自动销毁onUnmounted 防止组件卸载后继续执行
可单独启停精细化控制每个任务状态

🪄 性能与扩展建议

  • 若轮询任务较多且接口耗时大,可结合防抖节流Web Worker进行进一步优化;

  • 可结合 Pinia/Vuex 全局状态,在多组件间共享轮询结果;

  • 若希望在页面切换或标签页不可见时暂停,可配合 visibilitychange 事件使用。


🎬 总结

useMultiPolling 是一个轻量、灵活的 Vue3 Hook,
让你能以声明式方式管理多接口轮询,减少重复代码、提升可维护性

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐