Update AsyncTaskHelper.cs

- WaitEventAsync<T>() 增加可选 CancellationToken
- WaitSuccessOrFailureAsync<TSuccess, TFailure>() 增加可选 CancellationToken
- 保持现有调用兼容:原来的参数顺序和默认行为不变
- 抽出统一完成/清理逻辑:
    - 成功
    - 失败
    - 超时
    - 取消

- 防止重复完成导致重复 Unsubscribe
- 超时任务在事件完成后会被取消,避免后台 delay 继续跑
- 取消时会反订阅事件,适合后续 Procedure 生命周期迁移
This commit is contained in:
SepComet 2026-06-06 13:20:20 +08:00
parent 2a0cbc8f0e
commit 7ac38cd999
1 changed files with 123 additions and 29 deletions

View File

@ -17,13 +17,43 @@ namespace SepCore.AsyncTask
/// <param name="eventId">事件ID</param> /// <param name="eventId">事件ID</param>
/// <param name="predicate">事件过滤条件</param> /// <param name="predicate">事件过滤条件</param>
/// <param name="timeout">超时时间0表示不超时</param> /// <param name="timeout">超时时间0表示不超时</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>事件参数</returns> /// <returns>事件参数</returns>
public static UniTask<T> WaitEventAsync<T>(int eventId, Func<T, bool> predicate = null, float timeout = 0f) where T : GameEventArgs public static UniTask<T> WaitEventAsync<T>(
int eventId,
Func<T, bool> predicate = null,
float timeout = 0f,
CancellationToken cancellationToken = default) where T : GameEventArgs
{ {
var tcs = new UniTaskCompletionSource<T>(); var tcs = new UniTaskCompletionSource<T>();
var eventComponent = global::GameEntry.Event; var eventComponent = global::GameEntry.Event;
var timeoutCancellationTokenSource = timeout > 0f ? new CancellationTokenSource() : null;
CancellationTokenRegistration cancellationRegistration = default;
int isCompleted = 0;
EventHandler<GameEventArgs> handler = null; EventHandler<GameEventArgs> 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) => handler = (_, e) =>
{ {
var args = e as T; var args = e as T;
@ -31,25 +61,37 @@ namespace SepCore.AsyncTask
if (predicate != null && !predicate(args)) return; if (predicate != null && !predicate(args)) return;
eventComponent.Unsubscribe(eventId, handler); if (TryComplete())
{
tcs.TrySetResult(args); tcs.TrySetResult(args);
}
}; };
eventComponent.Subscribe(eventId, handler); eventComponent.Subscribe(eventId, handler);
// 超时处理 if (cancellationToken.CanBeCanceled)
if (timeout > 0f)
{ {
UniTask.Delay(TimeSpan.FromSeconds(timeout), cancellationToken: CancellationToken.None) cancellationRegistration = cancellationToken.Register(() =>
.ContinueWith(() =>
{ {
if (tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒"))) if (TryComplete(false))
{ {
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; return tcs.Task;
} }
@ -63,22 +105,49 @@ namespace SepCore.AsyncTask
/// <param name="successPredicate">成功事件过滤条件</param> /// <param name="successPredicate">成功事件过滤条件</param>
/// <param name="failurePredicate">失败事件过滤条件</param> /// <param name="failurePredicate">失败事件过滤条件</param>
/// <param name="timeout">超时时间0表示不超时</param> /// <param name="timeout">超时时间0表示不超时</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>成功事件参数</returns> /// <returns>成功事件参数</returns>
public static UniTask<TSuccess> WaitSuccessOrFailureAsync<TSuccess, TFailure>( public static UniTask<TSuccess> WaitSuccessOrFailureAsync<TSuccess, TFailure>(
int successEventId, int successEventId,
int failureEventId, int failureEventId,
Func<TSuccess, bool> successPredicate = null, Func<TSuccess, bool> successPredicate = null,
Func<TFailure, bool> failurePredicate = null, Func<TFailure, bool> failurePredicate = null,
float timeout = 0f) float timeout = 0f,
CancellationToken cancellationToken = default)
where TSuccess : GameEventArgs where TSuccess : GameEventArgs
where TFailure : GameEventArgs where TFailure : GameEventArgs
{ {
var tcs = new UniTaskCompletionSource<TSuccess>(); var tcs = new UniTaskCompletionSource<TSuccess>();
var eventComponent = global::GameEntry.Event; var eventComponent = global::GameEntry.Event;
var timeoutCancellationTokenSource = timeout > 0f ? new CancellationTokenSource() : null;
CancellationTokenRegistration cancellationRegistration = default;
int isCompleted = 0;
// 先声明两个 handler再赋值 lambda避免前向引用 // 先声明两个 handler再赋值 lambda避免前向引用
EventHandler<GameEventArgs> successHandler = null; EventHandler<GameEventArgs> successHandler = null;
EventHandler<GameEventArgs> failureHandler = null; EventHandler<GameEventArgs> 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) => successHandler = (_, e) =>
{ {
@ -87,9 +156,10 @@ namespace SepCore.AsyncTask
if (successPredicate != null && !successPredicate(args)) return; if (successPredicate != null && !successPredicate(args)) return;
eventComponent.Unsubscribe(successEventId, successHandler); if (TryComplete())
eventComponent.Unsubscribe(failureEventId, failureHandler); {
tcs.TrySetResult(args); tcs.TrySetResult(args);
}
}; };
failureHandler = (_, e) => failureHandler = (_, e) =>
@ -99,29 +169,53 @@ namespace SepCore.AsyncTask
if (failurePredicate != null && !failurePredicate(args)) return; if (failurePredicate != null && !failurePredicate(args)) return;
eventComponent.Unsubscribe(successEventId, successHandler); if (TryComplete())
eventComponent.Unsubscribe(failureEventId, failureHandler); {
tcs.TrySetException(new Exception($"操作失败: {args}")); tcs.TrySetException(new Exception($"操作失败: {args}"));
}
}; };
eventComponent.Subscribe(successEventId, successHandler); eventComponent.Subscribe(successEventId, successHandler);
eventComponent.Subscribe(failureEventId, failureHandler); eventComponent.Subscribe(failureEventId, failureHandler);
// 超时处理 if (cancellationToken.CanBeCanceled)
if (timeout > 0f)
{ {
UniTask.Delay(TimeSpan.FromSeconds(timeout), cancellationToken: CancellationToken.None) cancellationRegistration = cancellationToken.Register(() =>
.ContinueWith(() =>
{ {
if (tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒"))) if (TryComplete(false))
{ {
eventComponent.Unsubscribe(successEventId, successHandler); tcs.TrySetCanceled(cancellationToken);
eventComponent.Unsubscribe(failureEventId, failureHandler);
} }
}); });
} }
// 超时处理
if (timeoutCancellationTokenSource != null)
{
WaitTimeoutAsync(timeout, timeoutCancellationTokenSource.Token, () =>
{
if (TryComplete())
{
tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒"));
}
}).Forget();
}
return tcs.Task; 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();
}
} }
} }