* 异步回调模式
*/
// eslint-disable-next-line max-classes-per-file
export enum AsyncCallbackMode {
/** 执行所有回调 */
ALL = 'ALL',
/** 只执行最后一个回调 */
LAST_ONLY = 'LAST_ONLY',
/** 只执行第一个回调 */
FIRST_ONLY = 'FIRST_ONLY',
}
/**
* 异步状态
*/
export enum AsyncState {
/** 空闲 */
IDLE = 'IDLE',
/** 执行中 */
PENDING = 'PENDING',
/** 已完成 */
RESOLVED = 'RESOLVED',
/** 失败 */
REJECTED = 'REJECTED',
}
export abstract class ICheckable {
/** 是否有效(必须实现) */
abstract isValid(): boolean;
}
type AsyncOwner = ICheckable | cc.Node | cc.EventTarget;
/**
* 回调项
*/
interface CallbackItem<T> {
callback: (result?: T, error?: Error) => void;
owner?: AsyncOwner;
id?: string; // 回调唯一标识
priority?: number; // 优先级(数字越大优先级越高)
}
/**
* 异步状态管理器
* 管理异步操作的状态和回调队列
*/
/**
* 等待选项
*/
export interface AwaitOptions {
/** 回调唯一标识(可选)。如果已存在相同 id,会替换旧回调 */
id?: string;
/** 优先级(可选)。数字越大优先级越高,默认为 0 */
priority?: number;
/** 超时时间(毫秒)。超时后自动执行回调并传入 timeout 错误 */
timeout?: number;
}
export class AsyncStateManager<T = unknown> {
private state: AsyncState = AsyncState.IDLE;
private callbackQueue: Array<CallbackItem<T>> = [];
private callbackMode: AsyncCallbackMode = AsyncCallbackMode.ALL;
private result: T = null;
private error: Error = null;
private timeoutHandles: Map<string, number> = new Map(); // 超时定时器
private maxRetries: number = 0; // 最大重试次数
private retryCount: number = 0; // 当前重试次数
private retryDelay: number = 0; // 重试延迟(毫秒)
constructor(mode: AsyncCallbackMode = AsyncCallbackMode.ALL) {
this.callbackMode = mode;
}
/*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 状态查询
*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/
/** 是否空闲 */
isIdle(): boolean {
return this.state === AsyncState.IDLE;
}
/** 是否执行中 */
isPending(): boolean {
return this.state === AsyncState.PENDING;
}
/** 是否已完成 */
isResolved(): boolean {
return this.state === AsyncState.RESOLVED;
}
/** 是否失败 */
isRejected(): boolean {
return this.state === AsyncState.REJECTED;
}
/** 获取当前状态 */
getState(): AsyncState {
return this.state;
}
/** 获取结果(如果已完成) */
getResult(): T | null {
return this.result;
}
/** 获取错误(如果失败) */
getError(): Error | null {
return this.error;
}
/*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 回调管理
*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/
/**
* 等待异步完成
* @param callback 完成后的回调
* @param owner 回调的绑定对象(可选)。如果对象失效,则不执行回调
* @param options 等待选项(可选)
*/
await(callback: (result?: T, error?: Error) => void, owner?: AsyncOwner, options?: AwaitOptions): void {
// 已完成,检查 owner 有效性后执行回调
if (this.state === AsyncState.RESOLVED || this.state === AsyncState.REJECTED) {
if (this.isOwnerValid(owner)) {
callback(this.result, this.error);
} else {
cc.log('[AsyncStateManager] 绑定对象已失效,跳过回调');
}
return;
}
const id = options?.id;
const priority = options?.priority ?? 0;
const timeout = options?.timeout;
// 如果有 id,检查是否已存在相同 id 的回调
if (id) {
const existingIndex = this.callbackQueue.findIndex(item => item.id === id);
if (existingIndex !== -1) {
// 替换旧回调
cc.log(`[AsyncStateManager] 替换已存在的回调: ${id}`);
this.clearTimeout(id);
this.callbackQueue[existingIndex] = { callback, owner, id, priority };
} else {
// 添加新回调
this.callbackQueue.push({ callback, owner, id, priority });
}
} else {
// 没有 id,直接添加
this.callbackQueue.push({ callback, owner, id, priority });
}
// 设置超时
if (timeout && timeout > 0) {
this.setTimeout(id || `timeout_${Date.now()}`, timeout, callback, owner);
}
}
/**
* 等待异步完成(Promise 版本)
* @param owner 绑定对象(可选)
*/
awaitPromise(owner?: AsyncOwner): Promise<T> {
return new Promise((resolve, reject) => {
this.await((result, error) => {
if (error) {
reject(error);
} else {
resolve(result);
}
}, owner);
});
}
/*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 状态控制
*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/
/**
* 开始执行
*/
start(): void {
if (this.state === AsyncState.PENDING) {
cc.warn('[AsyncStateManager] 已经在执行中');
return;
}
this.state = AsyncState.PENDING;
this.result = null;
this.error = null;
}
/**
* 标记为完成
* @param result 结果
*/
resolve(result?: T): void {
if (this.state !== AsyncState.PENDING) {
cc.warn('[AsyncStateManager] 未处于 PENDING 状态,无法 resolve');
return;
}
this.state = AsyncState.RESOLVED;
this.result = result;
this.executeCallbacks();
}
/**
* 标记为失败
* @param error 错误
*/
reject(error: Error): void {
if (this.state !== AsyncState.PENDING) {
cc.warn('[AsyncStateManager] 未处于 PENDING 状态,无法 reject');
return;
}
this.state = AsyncState.REJECTED;
this.error = error;
this.executeCallbacks();
}
/**
* 重置状态
*/
reset(): void {
this.state = AsyncState.IDLE;
this.callbackQueue = [];
this.result = null;
this.error = null;
this.retryCount = 0;
this.clearAllTimeouts();
}
/*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 内部方法
*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/
/**
* 检查绑定对象是否有效
* @param owner 绑定对象
* @returns 是否有效
*/
private isOwnerValid(owner: AsyncOwner): boolean {
if (!owner) {
return true; // 未绑定对象,认为有效
}
// Cocos 节点检查
if (owner instanceof cc.Node) {
return cc.isValid(owner);
}
// Cocos 组件检查
if (owner instanceof cc.Component) {
return cc.isValid(owner) && cc.isValid(owner.node);
}
if (owner instanceof ICheckable)
// 普通对象(假设有效)
return owner.isValid();
}
/**
* 执行回调队列
*/
private executeCallbacks(): void {
if (this.callbackQueue.length === 0) {
return;
}
// 清除所有超时定时器
this.clearAllTimeouts();
// 过滤掉绑定对象已失效的回调
let validCallbacks = this.callbackQueue.filter((item) => this.isOwnerValid(item.owner));
if (validCallbacks.length === 0) {
cc.log('[AsyncStateManager] 所有绑定对象已失效,跳过回调');
this.callbackQueue = [];
return;
}
// 按优先级排序(优先级高的先执行),相同优先级保持原有顺序(稳定排序)
validCallbacks = validCallbacks.sort((a, b) => {
const priorityDiff = (b.priority || 0) - (a.priority || 0);
if (priorityDiff !== 0) {
return priorityDiff;
}
// 优先级相同时,保持原队列顺序(通过索引比较)
return this.callbackQueue.indexOf(a) - this.callbackQueue.indexOf(b);
});
switch (this.callbackMode) {
case AsyncCallbackMode.ALL:
// 执行所有有效回调(按优先级从高到低)
validCallbacks.forEach((item) => {
this.safeExecuteCallback(item);
});
break;
case AsyncCallbackMode.LAST_ONLY:
// 只执行最后一个有效回调(优先级最高的,相同优先级取最后添加的)
const lastItem = validCallbacks[validCallbacks.length - 1];
this.safeExecuteCallback(lastItem);
break;
case AsyncCallbackMode.FIRST_ONLY:
// 只执行第一个有效回调(优先级最高的,相同优先级取最先添加的)
const firstItem = validCallbacks[0];
this.safeExecuteCallback(firstItem);
break;
default:
cc.warn('[AsyncStateManager] 未知的回调模式:', this.callbackMode);
break;
}
// 清空队列
this.callbackQueue = [];
}
/**
* 安全执行回调(带异常捕获)
*/
private safeExecuteCallback(item: CallbackItem<T>): void {
try {
item.callback(this.result, this.error);
} catch (e) {
cc.error('[AsyncStateManager] 回调执行出错:', e);
}
}
/*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 超时管理
*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/
/**
* 设置超时定时器
*/
private setTimeout(id: string, timeout: number, callback: (result?: T, error?: Error) => void, owner?: AsyncOwner): void {
const handle = window.setTimeout(() => {
if (this.state === AsyncState.PENDING && this.isOwnerValid(owner)) {
cc.warn(`[AsyncStateManager] 回调超时 (${timeout}ms): ${id}`);
callback(null, new Error(`Timeout after ${timeout}ms`));
// 从队列中移除
this.callbackQueue = this.callbackQueue.filter(item => item.id !== id);
}
this.timeoutHandles.delete(id);
}, timeout);
this.timeoutHandles.set(id, handle);
}
/**
* 清除指定超时定时器
*/
private clearTimeout(id: string): void {
const handle = this.timeoutHandles.get(id);
if (handle !== undefined) {
window.clearTimeout(handle);
this.timeoutHandles.delete(id);
}
}
/**
* 清除所有超时定时器
*/
private clearAllTimeouts(): void {
this.timeoutHandles.forEach((handle) => {
window.clearTimeout(handle);
});
this.timeoutHandles.clear();
}
/*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 便捷方法
*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/
/**
* 包装异步操作(支持重试)
* @param asyncFunc 异步函数
*/
async execute(asyncFunc: () => Promise<T>): Promise<void> {
this.start();
await this.executeWithRetry(asyncFunc);
}
/**
* 带重试的执行
*/
private async executeWithRetry(asyncFunc: () => Promise<T>): Promise<void> {
try {
const result = await asyncFunc();
this.resolve(result);
this.retryCount = 0; // 重置重试次数
} catch (error) {
// 检查是否需要重试
if (this.retryCount < this.maxRetries) {
this.retryCount++;
cc.warn(`[AsyncStateManager] 执行失败,重试 ${this.retryCount}/${this.maxRetries}`, error);
// 延迟后重试
if (this.retryDelay > 0) {
await new Promise(resolve => setTimeout(resolve, this.retryDelay));
}
await this.executeWithRetry(asyncFunc);
} else {
// 重试次数用尽,标记为失败
this.reject(error as Error);
this.retryCount = 0;
}
}
}
/**
* 设置重试策略
* @param maxRetries 最大重试次数
* @param retryDelay 重试延迟(毫秒)
*/
setRetryStrategy(maxRetries: number, retryDelay: number = 0): void {
this.maxRetries = maxRetries;
this.retryDelay = retryDelay;
}
/**
* 设置回调模式
* @param mode 回调模式
*/
setMode(mode: AsyncCallbackMode): void {
this.callbackMode = mode;
}
/**
* 获取等待中的回调数量
*/
getQueueSize(): number {
return this.callbackQueue.length;
}
/**
* 移除指定对象的所有回调
* @param owner 绑定对象
*/
removeCallbacksByOwner(owner: AsyncOwner): void {
if (!owner) {
return;
}
const beforeCount = this.callbackQueue.length;
this.callbackQueue = this.callbackQueue.filter((item) => item.owner !== owner);
const afterCount = this.callbackQueue.length;
if (beforeCount !== afterCount) {
cc.log(`[AsyncStateManager] 移除了 ${beforeCount - afterCount} 个回调(owner 失效)`);
}
}
/**
* 清理所有失效对象的回调
*/
cleanInvalidCallbacks(): void {
const beforeCount = this.callbackQueue.length;
this.callbackQueue = this.callbackQueue.filter((item) => this.isOwnerValid(item.owner));
const afterCount = this.callbackQueue.length;
if (beforeCount !== afterCount) {
cc.log(`[AsyncStateManager] 清理了 ${beforeCount - afterCount} 个失效回调`);
}
}
/**
* 移除指定 id 的回调
* @param id 回调 id
*/
removeCallbackById(id: string): void {
if (!id) {
return;
}
this.clearTimeout(id);
const beforeCount = this.callbackQueue.length;
this.callbackQueue = this.callbackQueue.filter((item) => item.id !== id);
const afterCount = this.callbackQueue.length;
if (beforeCount !== afterCount) {
cc.log(`[AsyncStateManager] 移除回调: ${id}`);
}
}
/**
* 取消当前异步操作(不触发回调)
*/
cancel(): void {
if (this.state !== AsyncState.PENDING) {
return;
}
cc.log('[AsyncStateManager] 异步操作已取消');
this.state = AsyncState.IDLE;
this.callbackQueue = [];
this.clearAllTimeouts();
}
/**
* 强制完成(无论当前状态)
* @param result 结果
*/
forceResolve(result?: T): void {
this.state = AsyncState.RESOLVED;
this.result = result;
this.executeCallbacks();
}
/**
* 强制失败(无论当前状态)
* @param error 错误
*/
forceReject(error: Error): void {
this.state = AsyncState.REJECTED;
this.error = error;
this.executeCallbacks();
}
/**
* 链式调用:异步完成后执行另一个异步操作
* @param nextFunc 下一个异步函数
* @returns 新的 AsyncStateManager
*/
then<U>(nextFunc: (result: T) => Promise<U>): AsyncStateManager<U> {
const nextManager = new AsyncStateManager<U>(this.callbackMode);
this.await((result, error) => {
if (error) {
nextManager.reject(error);
} else {
nextManager.execute(() => nextFunc(result));
}
});
return nextManager;
}
/**
* 获取调试信息
*/
getDebugInfo(): string {
return `[AsyncStateManager] 状态=${this.state}, 队列=${this.callbackQueue.length}, 重试=${this.retryCount}/${this.maxRetries}`;
}
}
/*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 使用示例
*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/
/**
* 示例 1: 基础使用 - 手动控制状态
*
* const manager = new AsyncStateManager<string>();
*
* // 注册回调
* manager.await((result, error) => {
* if (error) {
* console.log('失败:', error.message);
* } else {
* console.log('成功:', result);
* }
* });
*
* // 开始异步操作
* manager.start();
*
* // 模拟异步操作
* setTimeout(() => {
* manager.resolve('操作完成');
* }, 1000);
*/
/**
* 示例 2: 使用 execute 方法 - 自动包装异步操作
*
* const manager = new AsyncStateManager<UserData>();
*
* // 先注册回调
* manager.await((userData, error) => {
* if (error) {
* console.log('加载用户数据失败:', error);
* } else {
* console.log('用户名:', userData.name);
* }
* });
*
* // 执行异步操作(自动处理 start/resolve/reject)
* manager.execute(async () => {
* const response = await fetch('/api/user');
* return await response.json();
* });
*/
/**
* 示例 3: 绑定对象生命周期
*
* const manager = new AsyncStateManager<string>();
* const node = new cc.Node();
*
* // 绑定到节点,节点销毁后回调不会执行
* manager.await((result) => {
* console.log('结果:', result);
* }, node);
*
* manager.execute(async () => {
* await delay(1000);
* return '完成';
* });
*
* // 如果在 1 秒内销毁节点,回调不会执行
* // node.destroy();
*/
/**
* 示例 4: 优先级控制
*
* const manager = new AsyncStateManager<number>();
*
* // 低优先级
* manager.await((result) => {
* console.log('低优先级回调:', result);
* }, null, { priority: 1 });
*
* // 高优先级(优先执行)
* manager.await((result) => {
* console.log('高优先级回调:', result);
* }, null, { priority: 10 });
*
* // 中优先级
* manager.await((result) => {
* console.log('中优先级回调:', result);
* }, null, { priority: 5 });
*
* manager.execute(async () => 42);
* // 输出顺序: 高优先级 -> 中优先级 -> 低优先级
*/
/**
* 示例 5: 回调 ID(防重复注册)
*
* const manager = new AsyncStateManager<string>();
*
* // 注册带 ID 的回调
* manager.await((result) => {
* console.log('第一次注册:', result);
* }, null, { id: 'my-callback' });
*
* // 相同 ID 会替换旧回调
* manager.await((result) => {
* console.log('第二次注册(会替换第一次):', result);
* }, null, { id: 'my-callback' });
*
* manager.execute(async () => '完成');
* // 只输出: "第二次注册(会替换第一次): 完成"
*/
/**
* 示例 6: 超时控制
*
* const manager = new AsyncStateManager<string>();
*
* // 设置 2 秒超时
* manager.await((result, error) => {
* if (error?.message.includes('Timeout')) {
* console.log('超时了!');
* } else {
* console.log('成功:', result);
* }
* }, null, { timeout: 2000 });
*
* // 模拟耗时操作(3 秒)
* manager.execute(async () => {
* await delay(3000);
* return '完成';
* });
* // 2 秒后会触发超时回调
*/
/**
* 示例 7: 重试策略
*
* const manager = new AsyncStateManager<string>();
*
* // 设置最多重试 3 次,每次间隔 1 秒
* manager.setRetryStrategy(3, 1000);
*
* manager.await((result, error) => {
* if (error) {
* console.log('最终失败:', error);
* } else {
* console.log('成功:', result);
* }
* });
*
* let attemptCount = 0;
* manager.execute(async () => {
* attemptCount++;
* if (attemptCount < 3) {
* throw new Error('模拟失败');
* }
* return '第三次成功';
* });
* // 会自动重试,第三次成功
*/
/**
* 示例 8: 回调模式 - LAST_ONLY
*
* const manager = new AsyncStateManager<string>(AsyncCallbackMode.LAST_ONLY);
*
* manager.await((result) => console.log('回调 1:', result));
* manager.await((result) => console.log('回调 2:', result));
* manager.await((result) => console.log('回调 3:', result));
*
* manager.execute(async () => '完成');
* // 只输出: "回调 3: 完成"
*/
/**
* 示例 9: 回调模式 - FIRST_ONLY
*
* const manager = new AsyncStateManager<string>(AsyncCallbackMode.FIRST_ONLY);
*
* manager.await((result) => console.log('回调 1:', result));
* manager.await((result) => console.log('回调 2:', result));
* manager.await((result) => console.log('回调 3:', result));
*
* manager.execute(async () => '完成');
* // 只输出: "回调 1: 完成"
*/
/**
* 示例 10: Promise 风格使用
*
* const manager = new AsyncStateManager<number>();
*
* manager.execute(async () => {
* await delay(1000);
* return 42;
* });
*
* // 使用 Promise 方式等待
* try {
* const result = await manager.awaitPromise();
* console.log('结果:', result);
* } catch (error) {
* console.log('错误:', error);
* }
*/
/**
* 示例 11: 链式调用
*
* const manager1 = new AsyncStateManager<number>();
*
* const manager2 = manager1.then(async (result) => {
* console.log('第一步结果:', result);
* return result * 2;
* });
*
* const manager3 = manager2.then(async (result) => {
* console.log('第二步结果:', result);
* return `最终值: ${result}`;
* });
*
* manager3.await((result) => {
* console.log(result); // "最终值: 20"
* });
*
* manager1.execute(async () => 10);
*/
/**
* 示例 12: 状态查询
*
* const manager = new AsyncStateManager<string>();
*
* console.log(manager.isIdle()); // true
* console.log(manager.isPending()); // false
*
* manager.start();
* console.log(manager.isPending()); // true
*
* manager.resolve('完成');
* console.log(manager.isResolved()); // true
* console.log(manager.getResult()); // "完成"
*/
/**
* 示例 13: 手动管理回调
*
* const manager = new AsyncStateManager<string>();
* const node = new cc.Node();
*
* manager.await(() => console.log('回调 1'), null, { id: 'callback1' });
* manager.await(() => console.log('回调 2'), node, { id: 'callback2' });
*
* console.log(manager.getQueueSize()); // 2
*
* // 移除指定 ID 的回调
* manager.removeCallbackById('callback1');
* console.log(manager.getQueueSize()); // 1
*
* // 移除指定对象的所有回调
* manager.removeCallbacksByOwner(node);
* console.log(manager.getQueueSize()); // 0
*/
/**
* 示例 14: 重置和取消
*
* const manager = new AsyncStateManager<string>();
*
* manager.await(() => console.log('这个不会执行'));
* manager.start();
*
* // 取消操作(不触发回调)
* manager.cancel();
* console.log(manager.isIdle()); // true
*
* // 或者重置状态
* manager.reset();
*/
/**
* 示例 15: 强制完成/失败
*
* const manager = new AsyncStateManager<string>();
*
* manager.await((result, error) => {
* if (error) {
* console.log('失败:', error.message);
* } else {
* console.log('成功:', result);
* }
* });
*
* // 不管当前状态,强制完成
* manager.forceResolve('强制完成');
*
* // 或强制失败
* // manager.forceReject(new Error('强制失败'));
*/
/**
* 示例 16: 在 Cocos Creator 组件中使用
*
* const { ccclass, property } = cc._decorator;
*
* @ccclass
* export default class GameController extends cc.Component {
* private loadManager = new AsyncStateManager<GameData>();
*
* onLoad() {
* // 绑定到组件生命周期
* this.loadManager.await((data, error) => {
* if (error) {
* this.showError(error.message);
* } else {
* this.initGame(data);
* }
* }, this);
*
* // 加载游戏数据
* this.loadGameData();
* }
*
* async loadGameData() {
* this.loadManager.setRetryStrategy(3, 1000);
* await this.loadManager.execute(async () => {
* const response = await fetch('/api/gamedata');
* return await response.json();
* });
* }
*
* onDestroy() {
* // 组件销毁时取消未完成的操作
* this.loadManager.cancel();
* }
* }
*/
/**
* 工具函数:延迟
*/
// function delay(ms: number): Promise<void> {
// return new Promise(resolve => setTimeout(resolve, ms));
// }