Obsługa wyjątków od synchronicznej części metody asynchronicznej
Mam do czynienia z sytuacją, w której zadanie, które rozpoczynam, może rzucać, a jednocześnie wykonywać synchronicznie na wątku początkowym. Coś takiego, w celach ilustracyjnych:
static async Task TestAsync()
{
var random = new Random(Environment.TickCount).Next();
if (random % 2 != 0)
throw new ApplicationException("1st");
await Task.Delay(2000);
Console.WriteLine("after await Task.Delay");
throw new ApplicationException("2nd");
}
Z kodu wywołującego chciałbym móc wyłapać wszelkie wyjątki, być może wyrzucone z części synchronicznej (tj. Doawait Task.Delay()
). Oto jak to obecnie robię:
static void Main(string[] args)
{
try
{
var task = TestAsync();
if (task.IsFaulted)
task.GetAwaiter().GetResult();
Console.WriteLine("TestAsync continues asynchronously...");
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
}
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
To działa, chociaż wygląda trochę kusząco, ponieważ nie maResult
naTask
.
Próbowałem teżtask.Wait()
zamiasttask.GetAwaiter().GetResult()
. To zawsze daje miAggregateException
które muszę odwijać (raczej niż się spodziewamApplicationException
bezpośrednio).
Czy są jakieś inne opcje?
[EDITED] Aby odnieść się do komentarzy: Robię to, ponieważ jeśli zadanie nie powiedzie się natychmiast, nie chcę dodawać go do listy oczekujących zadań, które utrzymuję. Samo zadanie nic nie wie o takiej liście (i nie musi). Nadal chcę rejestrować wyjątek i informować o tym użytkownika. Mogę też to zrobićthrow task.Exception
, ale to nie dałoby ramce stosu wyjątków przechwyconej za pomocąExceptionDispatchInfo
.
[AKTUALIZACJA] Zainspirowany innymi odpowiedziami i komentarzami: jeśli mam pełną kontrolęTestAsync
i nie chcę przedstawiać nowych członków klasy, mogę również zrobić coś takiego jak poniżej. Przydaje się przy walidacji argumentów:
static Task TestAsync(int delay)
{
if (delay < 0)
throw new ArgumentOutOfRangeException("delay");
Func<Task> asyncPart = async () =>
{
Console.WriteLine("await Task.Delay");
await Task.Delay(delay);
throw new ApplicationException("2nd");
};
return asyncPart();
}