PROMISE INTRODUZIONE

Nell’esempio precedente abbiamo effettuato due richieste asincrone, la prima per recuperare il post con ID=1 e la seconda per recuperare i commenti del post. Abbiamo gestito la richiesta asincrona con delle callback asincrone, ovvero invocate soltanto quando abbiamo una risposta alla nostra richiesta. Per fare la seconda richiesta (visualizzare i commenti) la prima (recupero post ID=1) deve andare a buon fine. C’è una concatenazione di eventi, se ad esempio il post nella richiesta non esiste non possiamo richiederne i commenti. Facciamo un esempio del mondo reale, chiediamo ad un amico di andarci a comprare un barattolo di marmellata perché noi non possiamo. Abbiamo bisogno della marmellata perché vogliamo fare una crostata da regalare a nostra madre. Gli eventi che si devono concatenare sono:

  • Il nostro amico deve andare a comprare la marmellata
  • Se la crostata viene bene la portiamo a nostra madre
Immagine 1

CONCATENAZIONE DI EVENTI

È necessario che tutte queste azioni concatenate vadano a buon fine. Vi è una concatenazione di eventi in cui il successivo deve avvenire solo se il precedente è andato a buon fine. Inoltre, quando chiedo all’amico di andare a comprare la marmellata, non aspetto ma faccio altre cose, non mi blocco nell’attesa della marmellata. Quindi ho fatto una richiesta asincrona. Promise (promessa) è la modalità introdotta in ES6 che gestisce il codice asincrono superando il meccanismo delle callback asincrone.

PROMISE

Una promise non è altro che un oggetto creato dalla constructor function Promise per la gestione del codice asincrono. Una promise può assumere lo stato:

  • Pending (pendente)
  • Fulfilled/resolved (risolta)
  • Rejected (rigettata)
Copy to Clipboard

GESTIRE RICHIESTE AJAX MULTIPLE CON LE PROMISE

In questa sezione facciamo una chiamata AJAX a JSON Placeholder per recuperare il post con ID=1 e solo se la richiesta va a buon fine ne facciamo un’altra per recuperare i commenti del post. Tutto questo sostituendo le callback con le promise. Creiamo in libpromise.js una funzione request che prenderà in input il tipo di richiesta (GET o POST) e l’URL. Tale funzione ritorna una Promise avente come parametri un riferimento alla funzione resolve e alla funzione reject.

Copy to Clipboard
Copy to Clipboard
Copy to Clipboard

GESTIRE RICHIESTE AJAX MULTIPLE CON PROMISE ALL

Promise.all restituisce una singola promise quando tutte le promise vengono risolte. Quando abbiamo una serie di promise strettamente collegate possiamo usare Promise.all. Creiamo due file di testo, f1.txt e f2.txt e inseriamo del contenuto fittizio. Facciamo la richiesta per i due file. Con Promise.all basta che una richiesta non vada a buon fine e lo stato delle singole promise passa a rejected. Proviamo a cambiare il nome di uno dei due file, questo darà un errore e si entrerà nel catch in quanto il file non verrà trovato. Anche il contenuto del file non modificato non verrà visualizzato.

Copy to Clipboard
Copy to Clipboard

RICHIESTE HTTP CON L’API FETCH

L’api fetch rappresenta un nuovo modo di effettuare richieste http, andando un po’ a superare le richieste AJAX. Supponiamo di voler leggere un file di testo, f1.txt.

const risposta = fetch(f1.txt);

console.log(risposta);

Dalla figura a fianco si evidenzia che le API fetch lavorano con le promise. Quindi possiamo gestire la risposta con then e catch.

immagine 2

Dal console.log(risposta) si vede che quello che otteniamo è un oggetto Response. Per quanto riguarda il corpo della risposta abbiamo un ReadableStream, che altro non è che un flusso di dati che necessita di conversione ad uno specifico formato. Con la proprietà ok possiamo sapere se la richiesta è andata a buon fine o meno. Per quanto riguarda le API fetch entriamo all’interno del catch solo se ci sono errori di rete, tutti gli altri errori devono essere gestiti nel then.

immagine 3
Copy to Clipboard

ASYNC/AWAIT-FUNZIONI ASINCRONE

La specifica ES2017 ha introdotto le funzioni asincrone con le keyword async/await. Le funzioni asincrone sono basate sulle promise. Per rendere una funzione asincrona anteponiamo alla keyword function async.

immagine 1

Dato che le promise lavorano in modo asincrono otteniamo la seguente sequenza:

  • Inizio
  • Fine
  • In test

All’interno di una funzione asincrona anzichè utilizzare sempre .then per la risoluzione della promise mettiamo la parola chiave await. Con riferimento alla funzione utenti riportata nel codice in basso con await diciamo al motore JS aspetta che la promise venga risolta poi metti il risultato in utentiJSON.

Copy to Clipboard

APPROFONDIMENTO AI

In JavaScript, le Promises e le keyword async e await sono strumenti fondamentali per gestire operazioni asincrone, migliorando la leggibilità e il controllo del flusso del codice rispetto alle classiche callback. Vediamo una descrizione dettagliata di ciascun concetto.

Le Promises

Una Promise in JavaScript rappresenta un’operazione che potrebbe completarsi in futuro, restituendo un risultato (successo) o fallendo (errore). Le Promises sono oggetti che possono essere in uno dei seguenti stati:

Pending (in attesa): L’operazione asincrona non è ancora terminata.

Fulfilled (completata con successo): L’operazione è terminata con successo e la Promise ha restituito un risultato.

Rejected (fallita): L’operazione è fallita e la Promise ha restituito un errore.

Creare una Promise

Per creare una Promise, utilizziamo il costruttore Promise che accetta una funzione con due parametri: resolve (da chiamare quando l’operazione va a buon fine) e reject (da chiamare in caso di errore).

Esempio:

const myPromise = new Promise((resolve, reject) => {
      let success = true;

      if (success) {
           resolve(“Operazione completata con successo“);
     } else {
           reject(“Operazione fallita“);
     }
});

Usare una Promise

Una volta creata, una Promise può essere gestita con i metodi .then(), .catch() e .finally():

.then() viene eseguito quando la Promise viene risolta (successo).

.catch() viene eseguito in caso di errore (rifiuto).

.finally() viene eseguito a prescindere dal risultato (successo o errore).

myPromise
.then((message) => {
console.log(message); // Output: “Operazione completata con successo”
})
.catch((error) => {
console.log(error); // Questo non sarà eseguito perché success è true
})
.finally(() => {
console.log(“Operazione conclusa“);
});

Async e Await

Le keyword async e await introducono una sintassi più pulita e leggibile per gestire Promises, permettendo di scrivere codice asincrono in modo simile a quello sincrono.

Funzione Async

Una funzione definita con la keyword async restituisce automaticamente una Promise. Dentro una funzione async, possiamo usare await per aspettare che una Promise venga risolta o rigettata.

Esempio:

async function myAsyncFunction() {
      returnCiao!“;
}

myAsyncFunction().then((result) => console.log(result)); // Output: “Ciao!”

Await

La keyword await può essere usata solo all’interno di una funzione async e sospende l’esecuzione del codice finché la Promise non viene risolta o rigettata.

Esempio:

async function fetchData() {
try {
      const result = await myPromise;
      console.log(result); // Output: “Operazione completata con successo”
} catch (error) {
      console.log(error); // Output: “Operazione fallita” in caso di errore
} finally {
      console.log(“Operazione conclusa“);
   }
}

fetchData();

In questo esempio, il codice attende la risoluzione di myPromise prima di continuare. Se la Promise viene risolta, il valore restituito viene assegnato alla variabile result. In caso di errore, viene catturato dal blocco catch.

Vantaggi di Async e Await

Leggibilità: Il codice asincrono scritto con async e await è più leggibile e meno “annidato” rispetto all’uso di Promises e callback.

Gestione degli errori: La gestione degli errori è più semplice grazie all’uso di try…catch.

Sincronicità apparente: Il codice scritto con async e await sembra sincrono, ma mantiene l’esecuzione asincrona in background.

Conclusione

L’uso delle Promises e delle keyword async e await è essenziale per gestire operazioni asincrone in JavaScript in modo efficace. Le Promises permettono di gestire più facilmente le operazioni che richiedono del tempo, mentre async e await offrono una sintassi più intuitiva e leggibile, riducendo la complessità e migliorando la gestione degli errori.

LINK AI POST PRECEDENTI

IL LINGUAGGIO JAVASCRIPT

LINK AL CODICE SU GITHUB

GITHUB