/** * 轮询器(指数退避+超时+可取消) * */ import { PaymentApi } from "./PaymentApi"; import type { QueryOrderData } from "./types"; import { ApiCode } from "../network/client/types"; export interface PollOptions { maxDurationMs: number; // 轮询总时长(例如 2分钟) startIntervalMs: number; // 初始间隔(例如 2s) maxIntervalMs: number; // 最大间隔(例如 10s) jitter?: boolean; // 抖动 } export class PaymentPoller { private cancelled = false; // 取消轮询 cancel() { this.cancelled = true; } // 轮询订单 async poll(orderId: string, opt: PollOptions, onTick?: (d: QueryOrderData) => void): Promise { const t0 = Date.now(); let interval = opt.startIntervalMs; while (!this.cancelled) { // 查询 const r = await PaymentApi.I.queryOrder(orderId); if (r.code === ApiCode.OK && r.data) { // 回调 onTick?.(r.data); if (["SUCCESS", "FAILED", "CANCELED", "EXPIRED"].indexOf(r.data.status) !== -1) { return r.data; } } // 退出条件 if (Date.now() - t0 > opt.maxDurationMs) return null; // 等待 await this.sleep(this.jitter(interval, opt)); // 每次轮询后,interval 会按照 1.5 倍增长,直到达到最大间隔 maxIntervalMs,避免在短时间内重复请求 interval = Math.min(interval * 1.5, opt.maxIntervalMs); } return null; } // 用来暂停一定的时间(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;