In questo articolo si vedrà un esempio di funzione asincrona in JavaScript.
I programmi sincroni (come quelli scritti in linguaggio C) sono quelli che svolgono semplicemente un’istruzione dopo l’altra, esattamente come il programmatore le ha scritte nel codice sorgente.
Questi programmi hanno la caratteristica di eseguire una istruzione alla volta, bloccandosi (a volte) senza rispondere alle richieste dell’utente, ad esempio quando incontrano un’istruzione che richiede un tempo lungo per la sua esecuzione (ad esempio, se si chiede di scaricare un file video dalla rete).
Le funzioni che bloccano l’esecuzione dell’intero programma sono dette funzioni bloccanti. Per fare un esempio, le funzioni di I/O sono solitamente bloccanti. Non si bloccano per colpa di un errore o per colpa del programmatore, ma perchè la CPU esegue solitamente un’istruzione alla volta.
Per questo motivo i programmi sincroni sono facili da leggere dall’uomo.
Oggi un’istruzione di tipo bloccante non causa il blocco dell’intero programma perchè i programmatori utilizzano delle tecniche di programmazione concorrente, ossia avviano diverse parti del programma in diversi thread, e fanno eseguire ogni thread in un diverso core della CPU.
La programmazione concorrente non è molto semplice e introduce nuovi problemi da risolvere (ad esempio: risorsa contesa, stallo, ecc.)
I concetti appena descritti sono familiari a chi programma in C++, Java, Python, e molti altri linguaggi simili.
Linguaggi asincroni
Usando il linguaggio JavaScript, invece, l’esecuzione può avvenire nel browser, un ambiente di esecuzione asincrono e come conseguenza le istruzioni possono essere eseguite in un ordine diverso da quello in cui sono state scritte dal programmatore.
In questi casi il programmatore non deve preoccuparsi di gestire l’esecuzione in diversi thread, tuttavia deve cercare di evitare di perdere il controllo sul flusso di esecuzione del programma (mantenere l’ordine delle istruzioni nel programma).
Per questo motivo i programmi JavaScript sono difficili da leggere dall’uomo.
Quando una funzione è detta sincrona?
Quando le istruzioni sono eseguite in ordine, ad esempio nel caso di due diverse righe di codice, quando la prima riga contiene un risultato che deve essere usato nella seconda riga.
In JavaScript però, è possibile anche eseguire funzioni asincrone, che cioè si sa quando si avviano, ma non quando ritornano il controllo del flusso (eventualmente ti avvisano quando hanno terminato il loro lavoro).
Esistono diversi metodi per gestire più facilmente le istruzioni asincrone:
- callback
- event handler
- promise
- metodo then()
- async/await
Callback
Un metodo di eseguire una funzione dopo l’altra è di chiamare una seconda funzione all’interno della prima funzione (questo sistema è detto callback).
Event handler
Anche un event handler è un tipo di funzione asincrona, nel senso che non sappiamo quando l’evento arriva, e quindi la funzione handler è asincrona.
Per questo motivo un handler si può vedere come una callback, cioè una funzione passata in un’altra funzione, in attesa che la prima termini un compito lungo da svolgere.
In passato tutte le parti asincrone in JavaScript erano realizzate tramite Callback, con grossi problemi di leggibilità e di risoluzione dei bug.
Per continuare ad usare le Callback, senza complicare il codice sorgente, si sono introdotte funzioni che restituiscono oggetti Promise, perché sono più facili da usare rispetto alle Callback
Promise
Promise
è un tipo di oggetto che momentaneamente è in sospeso (pending) e che in futuro potrebbe diventare di un altro tipo ben determinato (settled), a scelta tra due possibili eventualità:
- completamento di un’operazione asincrona
- fallimento di un’operazione asincrona (con errore specificato al momento)
Metodo then()
Dato che siamo spesso dei consumatori di Promise
, vediamo prima come si usano. Nel prossimo esempio, funzioneDownload
(funzione asincrona) restituirà un oggetto Promise
, quindi si potrebbe invocare in questo modo:
let risultato = funzioneDownload().then(seVaBene,seVaMale)
grazie a then()
vengono passate due funzioni Callback
seVaBene
seVaMale
funzioneDownload
restituisce immediatamente il controllo alla riga successiva, restituendo momentaneamente un oggetto Promise
. Grazie a then
, che possiede due argomenti, all’oggetto Promise
si applicheranno due funzioni di callback.
seVaBene(risultato
)
seVaMale(risultato
)
Non è obbligatorio usare then()
. Esiste un’alternativa
async e await
Ripetiamo il precedente esempio usando async
e await
async function f() { let risultato; try { let r1 = await funzioneDownload(); if (!r1) { throw new Error();} // con ipotesi che seVaBene e seVaMale siano sincrone risultato = seVaBene(r1); } catch (errore) { risultato = seVaMale(errore); } return risultato; } async function f() { let risultato; try { let r1 = await funzioneDownload(); // con ipotesi che anche seVaBene e seVaMale siano asincrone risultato = await seVaBene(r1); } catch (e) { risultato = await seVaMale(e); // r1 potrebbe non esistere } return risultato; }
async
e await
permettono di usare una funzione asincrona come se fosse sincrona.
Tuttavia usando then
è semplice immaginare come eseguirli in cascata (chained then), come in questo esempio:
let risultato = funzioneDownload() .then(seVaBene) .then(seBeneVa) .catch(seVaMale)