146 lines
5.1 KiB
C#
146 lines
5.1 KiB
C#
using System;
|
||
using System.Threading.Tasks;
|
||
using RimWorld;
|
||
using Verse;
|
||
|
||
namespace AIImages.Helpers
|
||
{
|
||
/// <summary>
|
||
/// Вспомогательный класс для правильной обработки асинхронных операций в RimWorld
|
||
/// Предотвращает fire-and-forget паттерн и обеспечивает централизованную обработку ошибок
|
||
/// </summary>
|
||
public static class AsyncHelper
|
||
{
|
||
/// <summary>
|
||
/// Выполняет асинхронную задачу с обработкой ошибок
|
||
/// </summary>
|
||
public static async Task RunAsync(Func<Task> taskFunc, string operationName = "Operation")
|
||
{
|
||
try
|
||
{
|
||
await taskFunc();
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
Log.Message($"[AI Images] {operationName} was cancelled");
|
||
Messages.Message($"{operationName} was cancelled", MessageTypeDefOf.RejectInput);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error($"[AI Images] Error in {operationName}: {ex.Message}\n{ex.StackTrace}");
|
||
Messages.Message(
|
||
$"Error in {operationName}: {ex.Message}",
|
||
MessageTypeDefOf.RejectInput
|
||
);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Выполняет асинхронную задачу с обработкой ошибок и callback при успехе
|
||
/// </summary>
|
||
public static async Task RunAsync<T>(
|
||
Func<Task<T>> taskFunc,
|
||
Action<T> onSuccess,
|
||
string operationName = "Operation"
|
||
)
|
||
{
|
||
try
|
||
{
|
||
T result = await taskFunc();
|
||
onSuccess?.Invoke(result);
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
Log.Message($"[AI Images] {operationName} was cancelled");
|
||
Messages.Message($"{operationName} was cancelled", MessageTypeDefOf.RejectInput);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error($"[AI Images] Error in {operationName}: {ex.Message}\n{ex.StackTrace}");
|
||
Messages.Message(
|
||
$"Error in {operationName}: {ex.Message}",
|
||
MessageTypeDefOf.RejectInput
|
||
);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Выполняет асинхронную задачу с полным контролем: onSuccess, onError, onCancel
|
||
/// </summary>
|
||
public static async Task RunAsync<T>(
|
||
Func<Task<T>> taskFunc,
|
||
Action<T> onSuccess,
|
||
Action<Exception> onError = null,
|
||
Action onCancel = null,
|
||
string operationName = "Operation"
|
||
)
|
||
{
|
||
try
|
||
{
|
||
T result = await taskFunc();
|
||
onSuccess?.Invoke(result);
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
Log.Message($"[AI Images] {operationName} was cancelled");
|
||
|
||
if (onCancel != null)
|
||
{
|
||
onCancel();
|
||
}
|
||
else
|
||
{
|
||
Messages.Message(
|
||
$"{operationName} was cancelled",
|
||
MessageTypeDefOf.RejectInput
|
||
);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Error($"[AI Images] Error in {operationName}: {ex.Message}\n{ex.StackTrace}");
|
||
|
||
if (onError != null)
|
||
{
|
||
onError(ex);
|
||
}
|
||
else
|
||
{
|
||
Messages.Message(
|
||
$"Error in {operationName}: {ex.Message}",
|
||
MessageTypeDefOf.RejectInput
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Безопасно выполняет Task без ожидания результата, с логированием ошибок
|
||
/// Используется когда нужен fire-and-forget, но с обработкой ошибок
|
||
/// </summary>
|
||
public static void FireAndForget(Task task, string operationName = "Background operation")
|
||
{
|
||
if (task == null)
|
||
return;
|
||
|
||
task.ContinueWith(
|
||
t =>
|
||
{
|
||
if (t.IsFaulted && t.Exception != null)
|
||
{
|
||
var ex = t.Exception.GetBaseException();
|
||
Log.Error(
|
||
$"[AI Images] Error in {operationName}: {ex.Message}\n{ex.StackTrace}"
|
||
);
|
||
}
|
||
else if (t.IsCanceled)
|
||
{
|
||
Log.Message($"[AI Images] {operationName} was cancelled");
|
||
}
|
||
},
|
||
TaskScheduler.Default
|
||
);
|
||
}
|
||
}
|
||
}
|