using System; using System.Threading; using Cysharp.Threading.Tasks; using GameFramework.Event; namespace SepCore.AsyncTask { /// /// 异步任务辅助类,用于将事件回调转换为 UniTask /// public static class AsyncTaskHelper { /// /// 等待指定事件触发 /// /// 事件参数类型 /// 事件ID /// 事件过滤条件 /// 超时时间(秒),0表示不超时 /// 取消令牌 /// 事件参数 public static UniTask WaitEventAsync( int eventId, Func predicate = null, float timeout = 0f, CancellationToken cancellationToken = default) where T : GameEventArgs { var tcs = new UniTaskCompletionSource(); var eventComponent = global::GameEntry.Event; var timeoutCancellationTokenSource = timeout > 0f ? new CancellationTokenSource() : null; CancellationTokenRegistration cancellationRegistration = default; int isCompleted = 0; EventHandler handler = null; void Cleanup(bool disposeCancellationRegistration) { eventComponent.Unsubscribe(eventId, handler); timeoutCancellationTokenSource?.Cancel(); timeoutCancellationTokenSource?.Dispose(); if (disposeCancellationRegistration) { cancellationRegistration.Dispose(); } } bool TryComplete(bool disposeCancellationRegistration = true) { if (Interlocked.Exchange(ref isCompleted, 1) != 0) { return false; } Cleanup(disposeCancellationRegistration); return true; } handler = (_, e) => { var args = e as T; if (args == null) return; if (predicate != null && !predicate(args)) return; if (TryComplete()) { tcs.TrySetResult(args); } }; eventComponent.Subscribe(eventId, handler); if (cancellationToken.CanBeCanceled) { cancellationRegistration = cancellationToken.Register(() => { if (TryComplete(false)) { tcs.TrySetCanceled(cancellationToken); } }); } // 超时处理 if (timeoutCancellationTokenSource != null) { WaitTimeoutAsync(timeout, timeoutCancellationTokenSource.Token, () => { if (TryComplete()) { tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒")); } }).Forget(); } return tcs.Task; } /// /// 等待成功或失败事件 /// /// 成功事件参数类型 /// 失败事件参数类型 /// 成功事件ID /// 失败事件ID /// 成功事件过滤条件 /// 失败事件过滤条件 /// 超时时间(秒),0表示不超时 /// 取消令牌 /// 成功事件参数 public static UniTask WaitSuccessOrFailureAsync( int successEventId, int failureEventId, Func successPredicate = null, Func failurePredicate = null, float timeout = 0f, CancellationToken cancellationToken = default) where TSuccess : GameEventArgs where TFailure : GameEventArgs { var tcs = new UniTaskCompletionSource(); var eventComponent = global::GameEntry.Event; var timeoutCancellationTokenSource = timeout > 0f ? new CancellationTokenSource() : null; CancellationTokenRegistration cancellationRegistration = default; int isCompleted = 0; // 先声明两个 handler,再赋值 lambda,避免前向引用 EventHandler successHandler = null; EventHandler failureHandler = null; void Cleanup(bool disposeCancellationRegistration) { eventComponent.Unsubscribe(successEventId, successHandler); eventComponent.Unsubscribe(failureEventId, failureHandler); timeoutCancellationTokenSource?.Cancel(); timeoutCancellationTokenSource?.Dispose(); if (disposeCancellationRegistration) { cancellationRegistration.Dispose(); } } bool TryComplete(bool disposeCancellationRegistration = true) { if (Interlocked.Exchange(ref isCompleted, 1) != 0) { return false; } Cleanup(disposeCancellationRegistration); return true; } successHandler = (_, e) => { var args = e as TSuccess; if (args == null) return; if (successPredicate != null && !successPredicate(args)) return; if (TryComplete()) { tcs.TrySetResult(args); } }; failureHandler = (_, e) => { var args = e as TFailure; if (args == null) return; if (failurePredicate != null && !failurePredicate(args)) return; if (TryComplete()) { tcs.TrySetException(new Exception($"操作失败: {args}")); } }; eventComponent.Subscribe(successEventId, successHandler); eventComponent.Subscribe(failureEventId, failureHandler); if (cancellationToken.CanBeCanceled) { cancellationRegistration = cancellationToken.Register(() => { if (TryComplete(false)) { tcs.TrySetCanceled(cancellationToken); } }); } // 超时处理 if (timeoutCancellationTokenSource != null) { WaitTimeoutAsync(timeout, timeoutCancellationTokenSource.Token, () => { if (TryComplete()) { tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒")); } }).Forget(); } return tcs.Task; } private static async UniTaskVoid WaitTimeoutAsync(float timeout, CancellationToken cancellationToken, Action onTimeout) { try { await UniTask.Delay(TimeSpan.FromSeconds(timeout), cancellationToken: cancellationToken); } catch (OperationCanceledException) { return; } onTimeout?.Invoke(); } } }