dev #4

Merged
caoxiaozhu merged 3 commits from dev into main 2026-07-16 11:04:35 +08:00
Showing only changes of commit 5a040366da - Show all commits

View File

@@ -1,20 +1,82 @@
import { useIntervalFn } from '@vueuse/core' import { onBeforeUnmount, onMounted, ref } from 'vue'
interface PollingOptions {
/** 启动后是否立即执行一次,默认立即执行。 */
immediate?: boolean
/** 页面不可见时是否暂停,默认暂停。 */
pauseWhenHidden?: boolean
/** 轮询函数抛错时的可选回调。 */
onError?: (error: unknown) => void
}
/** /**
* 轮询 composable * 轮询 composable
* 封装 useIntervalFn自动在组件卸载时清理 * 串行轮询:上一轮完成后才安排下一轮,避免慢请求重叠和旧响应覆盖。
* 页面进入后台时自动暂停,恢复可见后立即刷新;组件卸载时自动清理。
*/ */
export function usePolling(fn: () => void | Promise<void>, interval = 5000, immediate = true) { export function usePolling(
const { pause, resume } = useIntervalFn(fn, interval, { immediate }) fn: () => void | Promise<void>,
interval: number | (() => number) = 5000,
options: PollingOptions = {},
) {
const { immediate = true, pauseWhenHidden = true, onError } = options
const isActive = ref(false)
const isRunning = ref(false)
let timer: ReturnType<typeof setTimeout> | null = null
function clearTimer() {
if (!timer) return
clearTimeout(timer)
timer = null
}
function schedule() {
clearTimer()
if (!isActive.value) return
if (pauseWhenHidden && document.hidden) return
const delay = typeof interval === 'function' ? interval() : interval
timer = setTimeout(() => void run(), Math.max(0, delay))
}
async function run() {
clearTimer()
if (!isActive.value || isRunning.value) return
if (pauseWhenHidden && document.hidden) return
isRunning.value = true
try {
await fn()
} catch (error) {
onError?.(error)
} finally {
isRunning.value = false
schedule()
}
}
function stop() { function stop() {
pause() isActive.value = false
clearTimer()
} }
function start() { function start() {
if (immediate) fn() if (isActive.value) return
resume() isActive.value = true
if (immediate) void run()
else schedule()
} }
return { start, stop, pause, resume } function handleVisibilityChange() {
if (!pauseWhenHidden || !isActive.value) return
if (document.hidden) clearTimer()
else void run()
}
onMounted(() => document.addEventListener('visibilitychange', handleVisibilityChange))
onBeforeUnmount(() => {
stop()
document.removeEventListener('visibilitychange', handleVisibilityChange)
})
return { start, stop, run, isActive, isRunning }
} }