HttpClient GetAsync - Asynchronous Coding & Performance

Durante la scrittura di codice vi sara’ capitato diverse volte di dovere effettuare chiamate ad una serie di API per ottenere informazioni in merito a determinati servizi (anche sparsi per il mondo e non di vostra competenza) e vi sarete accorti che effettuare (male) queste chiamate porta il vostro codice ad un rallentamento.

In questo breve esempio verra’ mostrato come effettuare piu’ chiamate asincrone a diverse API ed “interrompere” resto del codice solamente quando ci serve veramente il valore di ritorno.

async/await

Per effettuare questo giochino dobbiamo effettuare chiamate async (Asynchronous) verso i diversi servizi API da interrogare. Nel codice d’esempio verranno chiamati quattro servizi “dummy api” trovati effettuando una breve ricerca in google.

Prima di entrare nel vivo dell’esempio riporto una sintesi dalla documentazione Microsoft in dalla paginas “Asynchronous programming with async and await” (link in fondo articolo)

The Task asynchronous programming model (TAP) provides an abstraction over asynchronous code. You write code as a sequence of statements, just like always. You can read that code as though each statement completes before the next begins. The compiler performs a number of transformations because some of those statements may start work and return a Task that represents the ongoing work.

That’s the goal of this syntax: enable code that reads like a sequence of statements, but executes in a much more complicated order based on external resource allocation and when tasks complete. It’s analogous to how people give instructions for processes that include asynchronous tasks. Throughout this article, you’ll use an example of instructions for making a breakfast to see how the async and await keywords make it easier to reason about code, that includes a series of asynchronous instructions.

C’e chi crede che utilizzare async/await porti complessita’ nel codice e lo evita. Inutile dire che per me non e’ cosi’. Il codice bisogna curarlo sempre e comunque. Scrivere qualche parola chiave in piu’ non ha mai ucciso nessuno soprattuto se questo porta a delle performance ulteriori.

using

In testa alla nostra classe dobbiamo inserire -se non gia’ presenti- questi tre using

1
2
3
using System;
using System.Net.Http;
using System.Threading.Tasks;

HttpClient GetAsync

Come detto in precedenza effettueremo chiamate async alle nostre API (in questo esempio torneremo una variabile string che rappresenta il json su cui potrete poi lavorare)

Se la chiamata non dovesse avere successo il valore di .IsSuccessStatusCode sara’ false e tornera’ il valore di default del tipo di ritorno.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
private async Task<string> GetAsync(string url)
{
    using (HttpClient client = new HttpClient())
    {
        using (HttpResponseMessage message = await client.GetAsync(url))
        {
            if (message.IsSuccessStatusCode)
            {
                var result = await message.Content.ReadAsStringAsync();
                return result;
            }
            else
            {
                return default(string);
            }
        }
    }
}

GetAsync

Ora non resta che vedere come scrivere il nostro codice per lanciare in “Asynchronous mode” le chiamate ed attendere tramite await il valore ritorno.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public async void DoTest()
{
    Task<string> message1Async = GetAsync("http://dummy.restapiexample.com/api/v1/employees");
    Task<string> message2Async = GetAsync("https://jsonplaceholder.typicode.com/todos");
    Task<string> message3Async = GetAsync("https://reqres.in/api/users");
    Task<string> message4Async = GetAsync("http://fakerestapi.azurewebsites.net/api/Activities");

    // you can do other code ...

    string message1 = await message1Async;

    // you can do other code based on message1 ...

    string message2 = await message2Async;

    // you can do other code based on message2 ...

    string message3 = await message3Async;

    // you can do other code based on message3 ...

    string message4 = await message4Async;

    // you can do other code based on message4 ...
    
}

Come avrete visto e’ davvero semplice utilizzare async/await per effettuare le chiamate HttpClient / GetAsync e questo puo’ portare dei benefici enormi a livello di performance.

Performance

Volete capire l’impatto sulle performance anche di questo preve esempio? Provate a modificarlo nel seguente modo e cronometrare le differenze

1
2
Task<string> messageXAsync = ... ;
string messageX = await messageXAsync;

Per fare un test serio, vi consiglio utilizzare API vere e non delle chiamate dummy. Per assurdo vi basterebba anche la chiamata ad una Azure Function su App Service gratuito in quanto necessita’ il tempo di salire a differenza di quella su un piano serio a pagamento.