/** * 轮询器(指数退避+超时+可取消) * */ import { PaymentApi } from "./PaymentApi"; import proto from 'db://assets/Scripts/proto/proto.pb.js'; export interface PollOptions { maxDurationMs: number; // 最大轮询时长(超过就自动退出)(例如 2分钟) startIntervalMs: number; // 第一次轮询等待的间隔(例如 2s) maxIntervalMs: number; // 最大允许的间隔,防止指数增长过大(例如 10s) jitter?: boolean; // 是否启用抖动(让等待时间带点随机性,避免所有客户端同一时间请求服务器) } export class PaymentPoller { private cancelled = false; private currentPollCount = 0; // 轮询次数 // 取消轮询 cancel() { this.cancelled = true; } // 获取当前轮询次数 public getCurrentPollCount(): number { return this.currentPollCount; } /** * 轮询订单 * @param orderId 要查询的订单 ID * @param opt 轮询配置(间隔、最大时长、是否抖动) * @param onTick 可选回调函数,每次轮询到结果时调用 * @returns 最终查询到的订单结果 ICSQueryOrderRes,或者 null(超时/被取消) */ async poll(orderId: string, opt: PollOptions, onTick?: (d: proto.cs.ICSQueryOrderRes) => void): Promise { const t0 = Date.now(); let interval = opt.startIntervalMs; while (!this.cancelled) { // 增加轮询次数 this.currentPollCount++; // 查询 let reqData = {orderId: orderId}; console.log("第 ", this.currentPollCount, " 次轮询订单的请求数据:", reqData); const r = await PaymentApi.I.queryOrder(reqData); console.log("第 ", this.currentPollCount, " 次轮询订单的响应数据:", r); if (r.code === proto.cs.EnmRetCode.SUCCESS && r.data) { // 回调 onTick?.(r.data); // 订单状态,-1:失败,0:成功,1:处理中 if ([-1, 0].indexOf(r.data.status) !== -1) { return r.data; } } // 轮询超时 if (Date.now() - t0 > opt.maxDurationMs) { let resultData: proto.cs.ICSQueryOrderRes = { status: 92, // 自定义的状态,代表客户端轮询超时 retCode: 0, balance: 0, vipExpire: 0, } return resultData; } // 等待 await this.sleep(this.jitter(interval, opt)); // 每次轮询后,interval 会按照 1.5 倍增长,直到达到最大间隔 maxIntervalMs,避免在短时间内重复请求 interval = Math.min(interval * 1.5, opt.maxIntervalMs); } // 至此,代表取消了轮询订单 let resultData: proto.cs.ICSQueryOrderRes = { status: 90, // 自定义的状态,代表取消 retCode: 0, balance: 0, vipExpire: 0, } return resultData; } // 用来暂停一定的时间(ms 毫秒),并让轮询进入等待状态 private sleep(ms: number) { return new Promise(res => setTimeout(res, ms)); } // 用于实现抖动(随机化轮询间隔) private jitter(base: number, opt: PollOptions) { if (!opt.jitter) return base; const delta = Math.min(500, Math.max(100, base * 0.1)); return base + (Math.random() * 2 - 1) * delta; } } export default PaymentPoller;