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();
}
}
}