67 Funzioni async

Una funzione async è ua funzione che esegue del codice in modo asincrono e restituisce una promise.

All’interno della funzione asincrona possiamo utilizzare il costrutto await che permette di mettere in pausa il la promise, fino a quando non abbiamo un esito di risoluzione della promise davanti alla quale mettiamo await. Indipendentemente che il risultato sia positivo o che venga rigettata con un risultato negativo.

Tale procedura ci permette di scrivere codice asincrono in modo sincrono, ovvero eseguito riga per riga come viene letto il codice, invece di utilizzare funzioni di callback.

Entrando nel pratico, abbiamo il nostro jsonplceholder per recuperare dati, utilizziamo le pagine degli album, in cui abbiamo l’id dell’utente, id e titolo dell’album e photos che comprende titolo della foto, l’url, thumbnail e titolo dell’album a cui appartiene la foto

const albumUrl = 'https://jsonplaceholder.typicode.com/albums';
const photoUrl = 'https://jsonplaceholder.typicode.com/photos';

L’obiettivo del nostro esercizio è avere gli album e per ogni album, tutte le foto associate. Come condizione vogliamo che finché non arrivano tutti gli album, non arriveranno le foto, quindi nessun dato sarà restituito fintanto che non avremo tutti i dati disponibili.

Avevamo visto come farlo con fetch, ora lo eseguiremo con async await, ma prima vediamo l’esempio con fetch, definiamo una funzione fetchAlbum() che si aspetta un album id e lanciamo il fetch passando l’url concatenando l’id

function fetchAlbum(id) {
    let album;
    fetch(albumUrl + '/' + id)
    return album;
}

abbiamo dichiarato una variabile album che verrà restituita dalla funzione. Concateniamo il then che restituisce una response che trasformiamo in json

.then(resp => resp.json() )

infine l’ultimo then ci restituirà il contenuto dell’album memorizzandolo nella variabile album

.then(albumcontent => album = albumcontent)

Se lanciamo la funzione fetchAlbum passando per esempio l’id 1 e stampiamo un alert, noteremo che il risultato sarà undefined

function fetchAlbum(id) {
    let album;
    fetch(albumUrl + '/' + id).then(resp => resp.json())
        .then(albumcontent => album = albumcontent)
    return album;
}

let res = fetchAlbum(1);
alert(res);

se analizziamo il Network nell’inspector, noteremo che la chiamata è andata a buon fine e i dati vengono restituiti correttamente, il problema è che essendo JavaScript sequenziale, la funzione fetchAlbum viene eseguita, ma il codice non si ferma per aspettare il risultato della promise dal server, il quale richiede più tempo e il codice restituisce subito il valore della variabile album che ancora non ne ha e restituisce undefined.

async

Mettendo la keyword async davanti al nome della funzione, indichiamo a js che stiamo usando una funzione asincrona, mantre con il costrutto await, indichiamo a js di aspettare nell’esecuzione del codice fintanto che la promise non viene risolta.

Racchiudiamo quindi la promise restituita dalla fetch dall’await e assegnamo il valore alla variabile album

album = await fetch(albumUrl + '/' + id).then(resp => resp.json())
    .then(albumcontent => albumcontent)

Avendolo assegnato in partenza il valore ad album, non deve essere più assegnato sull’ultimo then.

Se lasciassimo il codice solo così, otterremo un errore perchè se è vero che await attende il risultato della promise, è anche vero che await può essere utilizzato SOLO con una funzione async .

async function fetchAlbum(id) {
    let album;
    album = await fetch(albumUrl + '/' + id).then(resp => resp.json())
        .then(albumcontent => albumcontent)
    return album;
}

let res = fetchAlbum(1);
alert(res);

ora siamo in presenza di una funzione asincrona, js incontrando await sa che deve attendere la risoluzione della promise e una volta risolta assegnerà il valore alla variabile album, per poi proseguire con il codice.

async function fetchAlbum(id) {
    let album;
    album = await fetch(albumUrl + '/' + id).then(resp => resp.json())
        .then(albumcontent => albumcontent)
    console.log(album);
}

let res = fetchAlbum(1);
alert(res);

Ora nell’inspector avrò restituiti i dati della promise perchè una funzione async restituisce SEMPRE una promise il risultato in console di album è il risultato della risoluzione della promise. Togliamo la dichiarazione della variabile album, trasformandola direttamente in una costante e rimettiamo il return al posto del console.log, infine al posto dell’ alert mettiamo il console.log della risposta

async function fetchAlbum(id) {
    const album = await fetch(albumUrl + '/' + id).then(resp => resp.json())
        .then(albumcontent => albumcontent)
    return album;
}

let res = fetchAlbum(1);
res.then( resp => console.log(resp));

Qualora la promise fallisse, sarebbe comunque risolta, restituendo un errore che sarebbe passato ad album.

Quindi fintanto che la promise dell’avita non è risolta il codice non proseguirà, possiamo anche mettere più await i quali dovranno essere risolti a loro volta per far proseguire il codice

async function fetchAlbum(id) {
    const album = await fetch(albumUrl + '/' + id).then(resp => resp.json())
        .then(albumcontent => albumcontent);

    const album2 = await fetch(albumUrl + '/' + 2).then(resp => resp.json())
        .then(albumcontent => albumcontent);

    console.log(album2);
}

Il codice incontrando il primo await di album aspetta la sua risoluzione, per passare al successivo await del quale aspetta a sua volta la rispettiva risoluzione. Eliminando il secondo await, sarebbe una funzione normale e avremo il consol.log di una promise che sarà in stato pending

async function fetchAlbum(id) {
    const album = await fetch(albumUrl + '/' + id).then(resp => resp.json())
        .then(albumcontent => albumcontent);

    const album2 =  fetch(albumUrl + '/' + 2).then(resp => resp.json())
        .then(albumcontent => albumcontent);

    console.log(album2);
    return album;
}

let res = fetchAlbum(1);
res.then( resp => console.log(resp));

Risulta molto comodo usare le funzioni async con await, quando abbiamo diverse promise annidate e magari alcune promise hanno bisogno di risultati provenienti da altre per essere risolte, tipo per le photo associate ad un album id, prima devo attendere che la promise album venga risolta. Non sarebbe necessario usare await, potrei utilizzare diversi then annidati, ma con await il codice e la logica diventano più chiari.