MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Gestionarea erorilor în metodele asincrone

Gestionarea erorilor în metodele asincrone care utilizează cuvintele cheie async și await are propriile particularități.

Pentru gestionarea erorilor, expresia await este plasată într-un bloc try:

try
{
   await PrintAsync("Hello FDC.COM");
   await PrintAsync("Hi");
}
catch (Exception ex)
{
   Console.WriteLine(ex.Message);
}

async Task PrintAsync(string message)
{
   // dacă lungimea șirului este mai mică de 3 caractere, generăm o excepție
   if (message.Length < 3)
       throw new ArgumentException($"Invalid string length: {message.Length}");
   await Task.Delay(100);     // imitația unei operații de lungă durată
   Console.WriteLine(message);
}

În acest caz, metoda asincronă PrintAsync generează excepția ArgumentException dacă metoda primește un șir cu lungimea mai mică de 3 caractere.

Pentru gestionarea excepției în metoda Main, expresia await este plasată într-un bloc try. În rezultat, la executarea apelului await PrintAsync("Hi"), se va genera o excepție. Totuși, programul nu se va opri în mod avariat, ci va gestiona excepția și va continua calculul.

Afișarea pe consolă a programului:

Hello FDC.COM
Invalid string length: 2

Trebuie avut în vedere că dacă metoda asincronă are tipul void, atunci excepția nu se transmite în afara metodei, prin urmare, nu vom putea gestiona excepția la apelul metodei:

try
{
   PrintAsync("Hello FDC.COM");
   PrintAsync("Hi");       // aici programul va genera o excepție și se va opri avariat
   await Task.Delay(1000); // așteptăm finalizarea sarcinilor
}
catch (Exception ex)    // excepția NU va fi gestionată
{
   Console.WriteLine(ex.Message);
}

async void PrintAsync(string message)
{
   // dacă lungimea șirului este mai mică de 3 caractere, generăm o excepție
   if (message.Length < 3)
       throw new ArgumentException($"Invalid string length: {message.Length}");
   await Task.Delay(100);     // imitația unei operații de lungă durată
   Console.WriteLine(message);
}

În acest caz, deși metodele asincrone sunt apelate în blocul try, excepția nu va fi capturată și gestionată. Acesta este unul dintre dezavantajele utilizării metodelor asincrone de tip void. Totuși, în acest caz, putem defini gestionarea excepției în însăși metoda asincronă:

PrintAsync("Hello FDC.COM");
PrintAsync("Hi");
await Task.Delay(1000); // așteptăm finalizarea sarcinilor

async void PrintAsync(string message)
{
   try
   {
       // dacă lungimea șirului este mai mică de 3 caractere, generăm o excepție
       if (message.Length < 3)
           throw new ArgumentException($"Invalid string length: {message.Length}");
       await Task.Delay(100);     // imitația unei operații de lungă durată
       Console.WriteLine(message);
   }
   catch (Exception ex)
   {
       Console.WriteLine(ex.Message);
   }
}

Investigarea excepției

În caz de eroare, obiectul Task, care reprezintă sarcina asincronă în care a apărut eroarea, are proprietatea IsFaulted cu valoarea true. În plus, proprietatea Exception a obiectului Task conține toate informațiile despre eroare. Să inspectăm această proprietate:

var task = PrintAsync("Hi");
try
{
   await task;
}
catch
{
   Console.WriteLine(task.Exception?.InnerException?.Message); // Invalid string length: 2
   Console.WriteLine($"IsFaulted: {task.IsFaulted}");  // IsFaulted: True
   Console.WriteLine($"Status: {task.Status}");        // Status: Faulted
}

async Task PrintAsync(string message)
{
   // dacă lungimea șirului este mai mică de 3 caractere, generăm o excepție
   if (message.Length < 3)
       throw new ArgumentException($"Invalid string length: {message.Length}");
   await Task.Delay(1000);     // imitația unei operații de lungă durată
   Console.WriteLine(message);
}

Și dacă transmitem în metodă un șir cu lungimea mai mică de 3 caractere, task.IsFaulted va fi true.

Gestionarea mai multor excepții - WhenAll

Dacă așteptăm finalizarea mai multor sarcini simultan, de exemplu, cu ajutorul Task.WhenAll, putem obține mai multe excepții în același timp pentru fiecare sarcină executată. În acest caz, putem obține toate excepțiile din proprietatea Exception.InnerExceptions:

// definim și lansăm sarcinile
var task1 = PrintAsync("H");
var task2 = PrintAsync("Hi");
var allTasks = Task.WhenAll(task1, task2);
try
{
   await allTasks;
}
catch (Exception ex)
{
   Console.WriteLine($"Exception: {ex.Message}");
   Console.WriteLine($"IsFaulted: {allTasks.IsFaulted}");
   if(allTasks.Exception is not null)
   {
       foreach (var exception in allTasks.Exception.InnerExceptions)
       {
           Console.WriteLine($"InnerException: {exception.Message}");
       }
   }
}

async Task PrintAsync(string message)
{
   // dacă lungimea șirului este mai mică de 3 caractere, generăm o excepție
   if (message.Length < 3)
       throw new ArgumentException($"Invalid string: {message}");
   await Task.Delay(1000);     // imitația unei operații de lungă durată
   Console.WriteLine(message);
}

Aici, în două apeluri ale metodei PrintAsync sunt transmise valori evident incorecte. Astfel, la ambele apeluri se va genera o eroare.

Deși blocul catch, prin variabila Exception ex, va primi o singură excepție capturată, cu ajutorul colecției Exception.InnerExceptions, vom putea obține informații despre toate excepțiile apărute.

În final, la executarea acestei metode, vom obține următoarea afișare pe consolă:

Exception: Invalid string: H
IsFaulted: True
InnerException: Invalid string: H
InnerException: Invalid string: Hi
← Lecția anterioară Lecția următoare →