Update AsyncTaskHelper.cs
- WaitEventAsync<T>() 增加可选 CancellationToken
- WaitSuccessOrFailureAsync<TSuccess, TFailure>() 增加可选 CancellationToken
- 保持现有调用兼容:原来的参数顺序和默认行为不变
- 抽出统一完成/清理逻辑:
- 成功
- 失败
- 超时
- 取消
- 防止重复完成导致重复 Unsubscribe
- 超时任务在事件完成后会被取消,避免后台 delay 继续跑
- 取消时会反订阅事件,适合后续 Procedure 生命周期迁移
This commit is contained in:
parent
170ea690b2
commit
fbb9e1e068
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue