diff --git a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/AsyncTaskHelper.cs b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/AsyncTaskHelper.cs
index 001196d..e699d11 100644
--- a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/AsyncTaskHelper.cs
+++ b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/AsyncTaskHelper.cs
@@ -17,13 +17,43 @@ namespace SepCore.AsyncTask
/// 事件ID
/// 事件过滤条件
/// 超时时间(秒),0表示不超时
+ /// 取消令牌
/// 事件参数
- public static UniTask WaitEventAsync(int eventId, Func predicate = null, float timeout = 0f) where T : GameEventArgs
+ 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;
@@ -31,23 +61,35 @@ namespace SepCore.AsyncTask
if (predicate != null && !predicate(args)) return;
- eventComponent.Unsubscribe(eventId, handler);
- tcs.TrySetResult(args);
+ if (TryComplete())
+ {
+ tcs.TrySetResult(args);
+ }
};
eventComponent.Subscribe(eventId, handler);
- // 超时处理
- if (timeout > 0f)
+ if (cancellationToken.CanBeCanceled)
{
- UniTask.Delay(TimeSpan.FromSeconds(timeout), cancellationToken: CancellationToken.None)
- .ContinueWith(() =>
+ cancellationRegistration = cancellationToken.Register(() =>
+ {
+ if (TryComplete(false))
{
- if (tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒")))
- {
- eventComponent.Unsubscribe(eventId, handler);
- }
- });
+ tcs.TrySetCanceled(cancellationToken);
+ }
+ });
+ }
+
+ // 超时处理
+ if (timeoutCancellationTokenSource != null)
+ {
+ WaitTimeoutAsync(timeout, timeoutCancellationTokenSource.Token, () =>
+ {
+ if (TryComplete())
+ {
+ tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒"));
+ }
+ }).Forget();
}
return tcs.Task;
@@ -63,22 +105,49 @@ namespace SepCore.AsyncTask
/// 成功事件过滤条件
/// 失败事件过滤条件
/// 超时时间(秒),0表示不超时
+ /// 取消令牌
/// 成功事件参数
public static UniTask WaitSuccessOrFailureAsync(
int successEventId,
int failureEventId,
Func successPredicate = null,
Func failurePredicate = null,
- float timeout = 0f)
+ 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) =>
{
@@ -87,9 +156,10 @@ namespace SepCore.AsyncTask
if (successPredicate != null && !successPredicate(args)) return;
- eventComponent.Unsubscribe(successEventId, successHandler);
- eventComponent.Unsubscribe(failureEventId, failureHandler);
- tcs.TrySetResult(args);
+ if (TryComplete())
+ {
+ tcs.TrySetResult(args);
+ }
};
failureHandler = (_, e) =>
@@ -99,29 +169,53 @@ namespace SepCore.AsyncTask
if (failurePredicate != null && !failurePredicate(args)) return;
- eventComponent.Unsubscribe(successEventId, successHandler);
- eventComponent.Unsubscribe(failureEventId, failureHandler);
- tcs.TrySetException(new Exception($"操作失败: {args}"));
+ if (TryComplete())
+ {
+ tcs.TrySetException(new Exception($"操作失败: {args}"));
+ }
};
eventComponent.Subscribe(successEventId, successHandler);
eventComponent.Subscribe(failureEventId, failureHandler);
- // 超时处理
- if (timeout > 0f)
+ if (cancellationToken.CanBeCanceled)
{
- UniTask.Delay(TimeSpan.FromSeconds(timeout), cancellationToken: CancellationToken.None)
- .ContinueWith(() =>
+ cancellationRegistration = cancellationToken.Register(() =>
+ {
+ if (TryComplete(false))
{
- if (tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒")))
- {
- eventComponent.Unsubscribe(successEventId, successHandler);
- eventComponent.Unsubscribe(failureEventId, failureHandler);
- }
- });
+ 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();
+ }
}
}