dev #4
@@ -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 }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user