Cosa sono in JavaScript async e await e come cambia l’ uso delle Promise

Le Promise permettono di fare asynchronous operation, async e await estendono le Promise e danno la possibilità di scrivere codice maggiormente leggibile come fosse sincrono

Stefano Marchisio
4 min readAug 10, 2020

Introduzione

Con la versione ES8 sono state introdotte le keywords “async” e “await”, queste keywords rendono ancora più semplice la gestione di operazioni asincrone utilizzando le Promise di JavaScript.

Ma procediamo per gradi. Prima dell’avvento delle Promise le asynchronous operation dovevano essere fatte attraverso l’uso dei callback. Le Promise hanno poi introdotto il concetto di chaining che permette di concatenare i vari metodi: .then() e .catch(). In questo modo non si vedono più tutti quei callback annidati che rendendo il codice poco leggibile.

Con l’avvento di “async” e “await” lavoriamo sempre con le Promise, ma non siamo più obbligati ad avere il chaining. Il risultato finale sarà del codice che pur rimanendo asincrono, all’apparenza e come codice sincrono questo a scapito di una maggiore leggibilità. Per chi conosce .Net abbiamo le stesse funzionalità fornite dalle keywords“async” e “await” in C#

Async

La parola chiave “async” prima di una funzione fa si che una funzione restituisca sempre una Promise. Se si ritorna un valore, questo viene inserito automaticamente un una Promise risolta, oppure possiamo far ritornare una Promise direttamente.

Caso 1

async function f() {  return 1;}f().then(alert); // 1

Caso 2

async function f() {  return Promise.resolve(1);}f().then(alert); // 1

Await

La parola chiave “await” fa si che l’esecuzione del codice rimanga in attesa che la Promise ritorni un valore (venga risolta). Questo comunque non bloccherà il thread perché dietro alla quinte è comunque eseguita in modo asincrono.

Cosa molto importante può essere usata solo all’interno di una funzione decorata con “async”. Se la funzione non è decorata con “async”, non funziona!

async function f() {  let promise = new Promise((resolve, reject) => {    setTimeout(() => resolve(“done!”), 1000)  });  let result = await promise; // wait until the promise resolves (*)  alert(result); // “done!”}f();

L’esecuzione della funzione verrà messa in pausa alla linea (*) e verrà ripresa quando la Promise ritornerà il risultato. A questo punto verrà mostrato “done!”. Come detto questo non bloccherà il thread, dietro alla quinte è comunque eseguita in modo asincrono.

Errori

In una Promise “classica” il rifiuto o un eventuale eccezione viene catturata dal metodo .catch(). Con “async” e “await” è tutto molto più semplice, infatti basta usare un semplice try … catch che tutti noi già conosciamo. Se una Promise termina regolarmente ritorna un risultato, ma in caso di rifiuto verrà sollevata un eccezione come se ci fosse un istruzione throw. In questo caso il controllo passerà al blocco catch.

async function f() {  try {    let response = await fetch(‘http://no-such-url');  } catch(err) {    alert(err); // TypeError: failed to fetch  }}f();

I 2 spezzoni di codice sottostante sono equivalenti.

async function f() {  await Promise.reject(new Error(“Whoops!”));}async function f() {  throw new Error(“Whoops!”);}

Se omettiamo di inserire un blocco catch otterremo un “unhandled error” visibile nella console.

Vediamo un esempio più complesso

function who() {  return new Promise(resolve => {
setTimeout(() => {
resolve(' Robin Hood ');
}, 300);
});}function what() { return new Promise(resolve => {
setTimeout(() => {
resolve('è un personaggio dei fumetti');
}, 300);
});}function where() { return new Promise(resolve => {
setTimeout(() => {
resolve('e vive nel bosco');
}, 300);
});}async function msg() {
const a = await who();
const b = await what();
const c = await where();
console.log(`${ a } ${ b } ${ c }`);
}
msg(); // Robin Hood è un personaggio dei fumetti e vive nel bosco -
// after 900 msec

Nell’esempio sovrastante ciascun step viene eseguito sequenzialmente, ogni step attenderà che venga fornita la risposta dello step precedente. Se volessimo un’esecuzione parallela dovremmo modificarlo nel seguente modo.

async function msg() {
const [a, b, c] = await Promise.all([who(), what(), where()]);
console.log(`${ a } ${ b } ${ c }`);
}
msg(); // Robin Hood è un personaggio dei fumetti e vive nel bosco
// after 300 msec

Il metodo Promise.all() ritorna un array di valori risolti.

Conclusioni

Pur trattandosi di codice asincrono, perché JavaScript è un linguaggio single-thred, il codice scritto con “async” e “await” ha una forma più comprensibile simile al codice sincrono.

IMPORTANTE: Quello scritto nel presente articolo con un browser vecchio non vale perchè le Promise non sono supportate. Per cui non rimane che fare le asynchronous operation alla vecchia maniera: ovvero con le callback!

Per approfondire: Cosa sono le JavaScript Promise e cos’è il chaining

Se volete contattarmi il mio profilo Linkedin è il seguente: Stefano Marchisio: Consulente freelance Angular ASP.NET MVC C#

--

--