Files
18xchat/assets/Scripts/chat18x/payment/PaymentPoller.ts
T

57 lines
1.9 KiB
TypeScript
Raw Normal View History

2025-08-19 18:32:09 +08:00
/**
* 轮询器(指数退避+超时+可取消)
*
*/
import { PaymentApi } from "./PaymentApi";
2025-08-26 16:41:28 +08:00
import proto from 'db://assets/Scripts/proto/proto.pb.js';
2025-08-19 18:32:09 +08:00
export interface PollOptions {
maxDurationMs: number; // 轮询总时长(例如 2分钟)
startIntervalMs: number; // 初始间隔(例如 2s
maxIntervalMs: number; // 最大间隔(例如 10s
jitter?: boolean; // 抖动
}
export class PaymentPoller {
private cancelled = false;
// 取消轮询
cancel() { this.cancelled = true; }
// 轮询订单
2025-08-26 16:41:28 +08:00
async poll(orderId: string, opt: PollOptions, onTick?: (d: proto.cs.ICSQueryOrderRes) => void): Promise<proto.cs.ICSQueryOrderRes | null> {
2025-08-19 18:32:09 +08:00
const t0 = Date.now();
let interval = opt.startIntervalMs;
while (!this.cancelled) {
// 查询
2025-08-26 16:41:28 +08:00
let reqData = {orderId: orderId};
const r = await PaymentApi.I.queryOrder(reqData);
if (r.code === proto.cs.EnmRetCode.SUCCESS && r.data) {
2025-08-19 18:32:09 +08:00
// 回调
onTick?.(r.data);
2025-08-26 16:41:28 +08:00
if ([-1, 0].indexOf(r.data.status) !== -1) {
2025-08-19 18:32:09 +08:00
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;