await (Riferimenti per C#)

L'operatore await viene applicato a un'attività in un metodo asincrono per inserire un punto di sospensione nell'esecuzione del metodo fino al completamento dell'attività di cui si è in attesa. L'attività rappresenta il lavoro attualmente in fase di esecuzione.

È possibile usare await solo in un metodo asincrono modificato dalla parola chiave async. Tale metodo, definito usando il modificatore async e contenente di solito una o più espressioni await, viene denominato metodo asincrono.

Nota

Le parole chiave async e await sono state introdotte in C# 5. Per un'introduzione alla programmazione asincrona, vedere Asynchronous Programming with async and await (Programmazione asincrona con async e await).

L'attività a cui si applica l'operatore await viene in genere restituita da una chiamata a un metodo che implementa il modello asincrono basato su attività. Sono inclusi i metodi che restituiscono oggetti Task, Task<TResult> e System.Threading.Tasks.ValueType<TResult>.

Nell'esempio seguente il metodo HttpClient.GetByteArrayAsync restituisce un oggetto Task<byte[]>. L'attività è una promessa di produrre la matrice di byte effettiva una volta completata l'attività. L'operatore GetByteArrayAsync sospende l'esecuzione fino al completamento delle operazioni del metodo await. Nel frattempo, il controllo viene restituito al chiamante di GetPageSizeAsync. Al termine dell'esecuzione dell'attività, l'espressione await restituisce una matrice di byte.

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      string[] args = Environment.GetCommandLineArgs();
      if (args.Length > 1)
         GetPageSizeAsync(args[1]).Wait();
      else
         Console.WriteLine("Enter at least one URL on the command line.");
   }

   private static async Task GetPageSizeAsync(string url)  
   {  
       var client = new HttpClient();  
       var uri = new Uri(Uri.EscapeUriString(url));
       byte[] urlContents = await client.GetByteArrayAsync(uri);
       Console.WriteLine($"{url}: {urlContents.Length/2:N0} characters");  
   }  
}
// The following call from the command line:
//    await1 http://docs.microsoft.com
// displays output like the following: 
//   http://docs.microsoft.com: 7,967 characters


Importante

Per l'esempio completo, vedere Procedura dettagliata: accesso al Web con async e await. È possibile scaricare l'esempio da Developer Code Samples (Esempi di codice per sviluppatori) del sito Web Microsoft. L'esempio è nel progetto AsyncWalkthrough_HttpClient.

Come mostrato nell'esempio precedente, se await viene applicato al risultato di una chiamata a un metodo che restituisce un elemento Task<TResult>, il tipo dell'espressione await è TResult. Se await viene applicato al risultato di una chiamata a un metodo che restituisce un elemento Task, il tipo dell'espressione await è void. Nell'esempio che segue viene illustrata la differenza.

// await keyword used with a method that returns a Task<TResult>.  
TResult result = await AsyncMethodThatReturnsTaskTResult();  

// await keyword used with a method that returns a Task.  
await AsyncMethodThatReturnsTask();  

// await keyword used with a method that returns a ValueTask<TResult>.
TResult result = await AsyncMethodThatReturnsValueTaskTResult();

Un'espressione await non blocca il thread su cui è in esecuzione. Comporta invece la registrazione, tramite il compilatore, della parte restante del metodo asincrono come continuazione dell'attività di cui si è in attesa. Il controllo quindi viene restituito al chiamante del metodo asincrono. Al termine dell'attività, ne richiama la continuazione e l'esecuzione del metodo asincrono riprende da dove era stata interrotta.

Un'espressione await può trovarsi solo nel corpo di un metodo che la contiene, di un'espressione lambda o di un metodo anonimo, che deve essere contrassegnato con un modificatore async. Il termine await funge da parola chiave solo in tale contesto. Altrove, viene interpretata come identificatore. All'interno del metodo, dell'espressione lambda o del metodo anonimo, un'espressione await non può trovarsi nel corpo di una funzione sincrona, in un'espressione di query, nel blocco di un'istruzione lock o in un contesto non sicuro.

Eccezioni

La maggior parte dei metodi asincroni restituisce Task o Task<TResult>. Le proprietà dell'attività restituita contengono informazioni sullo stato e sulla cronologia, ad esempio se l'attività è stata completata, se il metodo asincrono ha generato un'eccezione o è stato annullato e il risultato finale. L'operatore await accede a tali proprietà chiamando i metodi sull'oggetto restituito dal metodo GetAwaiter.

Se si è in attesa di un metodo asincrono che restituisce un'attività che genera un'eccezione, l'operatore await genera di nuovo l'eccezione.

Se si è in attesa di un metodo asincrono che restituisce un'attività che è stato annullato, tramite l'operatore await viene rigenerata un'eccezione OperationCanceledException.

Una singola attività in uno stato di errore può rispecchiare più eccezioni. Ad esempio, l'attività può essere il risultato di una chiamata a Task.WhenAll. Quando si attende tale attività, l'operazione await rigenera solo una delle eccezioni. Tuttavia, non è possibile prevedere quale delle eccezioni verrà rigenerata.

Per esempi sulla gestione degli errori nei metodi asincroni, vedere try-catch.

Esempio

L'esempio seguente restituisce il numero totale di caratteri nelle pagine di cui gli URL vengono passati come argomenti della riga di comando. L'esempio chiama il metodo GetPageLengthsAsync, contrassegnato con la parola chiave async. Il metodo GetPageLengthsAsync usa a sua volta la parola chiave await per attendere le chiamate del metodo HttpClient.GetStringAsync.

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

class Example
{
   static void Main()
   {
      string[] args = Environment.GetCommandLineArgs();
      if (args.Length < 2)
         throw new ArgumentNullException("No URLs specified on the command line.");
      
      long characters = GetPageLengthsAsync(args).Result;
      Console.WriteLine($"{args.Length - 1} pages, {characters:N0} characters");
   }

   private static async Task<long> GetPageLengthsAsync(string[] args)
   {
      var client = new HttpClient();
      long pageLengths = 0;

      for (int ctr = 1; ctr < args.Length; ctr++) {
         var uri = new Uri(Uri.EscapeUriString(args[ctr]));
         string pageContents = await client.GetStringAsync(uri);
         Interlocked.Add(ref pageLengths, pageContents.Length);
      }
      return pageLengths;
   }
}

Dato che l'uso di async e await nel punto di ingresso di un'applicazione non è supportato, non è possibile applicare l'attributo async al metodo Main e neanche attendere la chiamata del metodo GetPageLengthsAsync. È possibile assicurarsi che il metodo Main resti in attesa del completamento dell'operazione asincrona recuperando il valore della proprietà Task<TResult>.Result. Per le attività che non restituiscono un valore, è possibile chiamare il metodo Task.Wait.

Vedere anche

Asynchronous Programming with async and await (Programmazione asincrona con async e await)
Procedura dettagliata: accesso al Web con async e await
async