vue3 之异步轮训 hook 封装
背景
开发中经常会遇到支付
,认证
等场景,需要轮训接口获取当前的状态
代码
import { onMounted, onUnmounted, readonly, ref } from 'vue'/*** 函数轮训* 场景: 人脸认证,支付等场景*/interface UsePollingOptions<T = any> {maxPollingCount?: number // 最大轮训次数pollingTime?: number // 轮训时间间隔immediate?: boolean // 是否立即执行pollingFn: (...args: any) => Promise<{isEnd: boolean // 是否结束轮训data?: T // 轮训数据}> // 轮训函数onSuccess?: (data: T) => void // 成功回调onError?: (error: any) => void // 失败回调onComplete?: () => void // 失败、成功、超时都会走onTimeout?: () => void // 超时回调
}export function usePolling<T = any>(options: UsePollingOptions<T>) {const maxPollingCount = ref(options.maxPollingCount || 100)const pollingTime = ref(options.pollingTime || 1000)const timer = ref<NodeJS.Timeout | null>(null)const pollingCount = ref(0) // 当前轮训的次数const isLoading = ref(false) // 是否正在加载const error = ref<any>(null) // 错误信息const isPolling = ref(false) // 是否正在轮询/*** 启动轮训*/const polling = async () => {if (isPolling.value) {console.warn('轮询已在进行中,请勿重复启动')return}isPolling.value = trueisLoading.value = trueerror.value = nullpollingCount.value = 0timer.value = setInterval(async () => {if (pollingCount.value >= maxPollingCount.value) {// 超过最大轮训次数isLoading.value = falseoptions.onTimeout?.()options.onComplete?.()stopPolling()return}try {// 每次轮询时动态获取最新参数const res = await options.pollingFn()if (res.isEnd) {// 认证成功或失败时设置 isLoading 为 falseisLoading.value = falseif (res.data !== undefined) {options.onSuccess?.(res.data)}options.onComplete?.()// 结束轮训stopPolling()} else {// 继续轮询时保持 isLoading 为 trueisLoading.value = true}pollingCount.value++}catch (err) {console.error('轮询过程中发生错误:', err)error.value = err// 认证失败时设置 isLoading 为 falseisLoading.value = falseoptions.onError?.(err)options.onComplete?.()stopPolling()}}, pollingTime.value)}/*** 停止轮训*/const stopPolling = () => {if (timer.value) {clearInterval(timer.value)timer.value = null}isPolling.value = falseisLoading.value = false}/*** 重置轮训*/const resetPolling = () => {stopPolling()pollingCount.value = 0error.value = nullisLoading.value = false}onMounted(() => {if (options.immediate) {polling()}})onUnmounted(() => {stopPolling()})return {polling,stopPolling,resetPolling,isLoading,isPolling,error,pollingCount: readonly(pollingCount), // 当前轮训的次数}
}
组件中使用
const { polling, isLoading } = usePolling<FaceAuthPollResult>({maxPollingCount: 3,pollingTime: 2000,pollingFn: async () => {const res = await userAPI.pollFaceAuthResult({params: { businessId: businessId.value },})if (res.status === 0) {return {isEnd: res.data.isEnd,data: res.data,}}return {isEnd: true,}},onSuccess: (data) => {if (data.success) {message.success('认证成功')visible.value = false} else {message.error(data.msg || '认证失败')}},onTimeout() {message.error('认证超时')},
})const start = () => {polling()
}