如何更好的封装一个接口轮询?

相信大家做pc端开发的时候都会遇到一个需求,比如扫码登录的时候,通过手机端扫码之后一般都需要通过轮询查询登录状态,其实这是个很常见的需求,但是没做过的小伙伴可能就会不经意间写出一个bug。就是比如1秒钟轮询一次的时候,如果上次的接口还没成功响应那么下一次的接口就又发送出去了。正确做法应该是等待上个响应后再发出下一次请求。

其实解决bug的关键就是如何等待上次请求响应之后才发出下一次的请求,等待具体应该怎么实现呢?可以参考下我的思路。

具体如下:
ts 复制代码
type PollingEvent = 'success' | 'error' | 'stop';

interface PollingOptions {
 interval?: number; // 轮询间隔时间
 retries?: number; // 接口失败后重试次数
 immediate?: boolean; // 是否立即调用一次接口
}

type EventCallback<T> = (data?: T | Error) => void;

class PollingService<T = any> {
 private requestFn: () => Promise<T>;
 private interval: number;
 private retries: number;
 private immediate: boolean;

 private timer: ReturnType<typeof setTimeout> | null = null;
 private isPolling: boolean = false;
 private currentRetry: number = 0;
 private listeners = new Map<PollingEvent, Set<EventCallback<T>>>();

 constructor(requestFn: () => Promise<T>, options: PollingOptions = {}) {
   this.requestFn = requestFn;
   this.interval = options.interval || 1500;
   this.retries = options.retries || 0;
   this.immediate = options.immediate ?? true;
 }

 on(event: PollingEvent, callback: EventCallback<T>): () => void {
   if (!this.listeners.has(event)) {
     this.listeners.set(event, new Set());
   }
   const listeners = this.listeners.get(event)!;
   listeners.add(callback);

   return () => listeners.delete(callback);
 }

 start(): void {
   if (this.isPolling) return;
   this.isPolling = true;
   this.currentRetry = 0;

   if (this.immediate) {
     this.execute();
   } else {
     this.scheduleNext();
   }
 }

 stop(): void {
   this.isPolling = false;
   this.clearTimer();
   this.emit('stop');
 }

 private async execute(): Promise<void> {
   if (!this.isPolling) return;
   
   // 其实关键就在这里了,我们借助 async/await来实现
   try {
     const data = await this.requestFn();
     this.currentRetry = 0;
     this.emit('success', data);
   } catch (error) {
     this.handleError(error as Error);
     return;
   }

   this.scheduleNext();
 }

 private handleError(error: Error): void {
   this.emit('error', error);

   if (this.currentRetry < this.retries) {
     this.currentRetry++;
     this.execute(); // 立即重试
     return;
   }

   this.stop();
 }

 private scheduleNext(): void {
   if (!this.isPolling) return;

   this.clearTimer();
   this.timer = setTimeout(() => {
     this.execute();
   }, this.interval);
 }

 private clearTimer(): void {
   if (this.timer) {
     clearTimeout(this.timer);
     this.timer = null;
   }
 }

 private emit(event: PollingEvent, data?: T | Error): void {
   const listeners = this.listeners.get(event);
   if (listeners) {
     listeners.forEach((callback) => callback(data));
   }
 }
}

export { PollingService };

完成上述代码之后,在页面中直接引入使用即可。

使用如下:
ts 复制代码
import React, { useEffect, useRef } from 'react';
import { PollingService } from 'xxx/PollingService';

const Demo = () => {
    const pollingRef = useRef<any>(null);
    
    // 轮询的方法
    const requesFun = async () => {
        const res = await axios.post('xxx')
        // todo sth
    }
    
    useEffect(() => {
        pollingRef.current = new PollingService()
        // 启动轮询
        pollingRef.current.start()
        
        // !!!组件卸载一定要关闭轮询
        return () => {
            pollingRef?.current?.stop()
        }
    }, [)
    <div>测试页面</div>
}
export default Demo;
相关推荐
方也_arkling38 分钟前
别名路径联想提示。@/统一文件路径的配置
前端·javascript
毕设源码-朱学姐41 分钟前
【开题答辩全过程】以 基于web教师继续教育系统的设计与实现为例,包含答辩的问题和答案
前端
qq_177767371 小时前
React Native鸿蒙跨平台剧集管理应用实现,包含主应用组件、剧集列表、分类筛选、搜索排序等功能模块
javascript·react native·react.js·交互·harmonyos
qq_177767371 小时前
React Native鸿蒙跨平台自定义复选框组件,通过样式数组实现选中/未选中状态的样式切换,使用链式调用替代样式数组,实现状态驱动的样式变化
javascript·react native·react.js·架构·ecmascript·harmonyos·媒体
web打印社区1 小时前
web-print-pdf:突破浏览器限制,实现专业级Web静默打印
前端·javascript·vue.js·electron·html
RFCEO1 小时前
前端编程 课程十三、:CSS核心基础1:CSS选择器
前端·css·css基础选择器详细教程·css类选择器使用方法·css类选择器命名规范·css后代选择器·精准选中嵌套元素
烬头88212 小时前
React Native鸿蒙跨平台采用了函数式组件的形式,通过 props 接收分类数据,使用 TouchableOpacity实现了点击交互效果
javascript·react native·react.js·ecmascript·交互·harmonyos
Amumu121382 小时前
Vuex介绍
前端·javascript·vue.js
We་ct2 小时前
LeetCode 54. 螺旋矩阵:两种解法吃透顺时针遍历逻辑
前端·算法·leetcode·矩阵·typescript
qq_177767372 小时前
React Native鸿蒙跨平台通过Animated.Value.interpolate实现滚动距离到动画属性的映射
javascript·react native·react.js·harmonyos