Files
18xchat/assets/Scripts/chat18x/payment/PaymentPoller.ts
T
2025-08-19 18:32:31 +08:00

58 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 轮询器(指数退避+超时+可取消)
*
*/
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<QueryOrderData | null> {
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;