Categorie
Javascript Programmazione

{JS} API Promise: Le promise

Cosa sono le promises

Quando scriviamo istruzioni in javascript queste vengono eseguite in sequenze, ossia una riga alla volta.
Ciò vuol dire che ogni riga deve essere interamente eseguita prima di procedere a quella successiva, stiamo parlando di programmazione sincrona.

Nella programmazione asincrona invece si può passare alle righe successive anche se quelle precedenti non hanno terminato l’esecuzione.

Il concetto di promise in javascript è simile a quello di callback.

E’ come se nel programma che stiamo compilando inserissimo un segnaposto, una “promessa” da parte del programma, che ci garantisce che non appena sarà possibile, sarà restituito un dato/ un valore.

Di una promise non possiamo sapere se otterremo una risposta e non conosciamo il tipo di valore che sarà restituito.

Nel frattempo ci è fornita la possibilità di proseguire con le righe successive.

Si tratta di elaborazioni asincrone e differite.

Le promises fanno quindi parte del “asynchronous programming” ovvero della programmazione asincrona.

Sintassi delle promises

Prima dell’introduzione delle promise questo meccanismo era gestito attraverso una concatenazione di funzioni di callback:

scegliCondimento(function(condimenti){
faiOrdine(condimenti, function(ordine){
prendiOrdine(ordine, function(pizza){
mangiaPizza(pizza);
}, callBackFallita);
}, callBackFallita);
}, callBackFallita);

L’obiettivo è quello di eseguire la funzione “mangiaPizza(pizza)” ma per farlo abbiamo dovuto concatenare/incapsulare la stessa all’interno di molte altre funzioni necessarie per arrivare al nostro obiettivo.

scegliCondimento()
.then(function(condimenti){
return faiOrdine(condimenti);
})
.then(function(ordine){
return prendiOrdine(ordine);
})
.then(function(pizza){
return mangiaPizza(pizza);
})
.catch(callBackFallita);

Questa è la sintassi invece prevista delle promise; la funzione principale rimane “scegliCondimento()”; l’obbiettivo è sempre “mangiaPizza()”;
“scegliCondimento()” al suo interno ha una concatenazione di “.then()” letteralmente tradotto “quando>quando>quando”.
Ancora più letteralmente “quando fai questa questa funzione restituisci il risultato di quest’altra funzione”.
“.catch()” invece sostituisce tutte le funzioni di “callbackFallita” in coda presenti nell’esempio precedente raggruppandoli in un unica riga di comando.

scegliCondimento()
.then((condimenti) => return faiOrdine(condimenti);)
.then((ordine) => return prendiOrdine(ordine);)
.then((pizza)=> return mangiaPizza(pizza);)
.catch(callBackFallita);

Con l’utilizzo delle arrow-function il tutto risulta più compatto e facilmente leggibile.

scegliCondimento().then((condimenti) => return faiOrdine(condimenti);).then((ordine) => return prendiOrdine(ordine);).then((pizza)=> return mangiaPizza(pizza);).catch(callBackFallita);

In realtà avremmo potuto eseguire il tutto sulla stessa riga come nell’esempio qui sopra.

Chaining

Il “chaining” è il meccanismo mediante il quale in una concatenazione di .then() il valore di ritorno di un .then() precedente è passato automaticamente al .then() successivo.
Se siamo stati attenti negli esempi precedenti avremmo dovuto chiederci dove va a finire il ritorno del valore di una funzione contenuta in un .then(); questo viene passato automaticamente al .then() successivo.

..
.then(...
VALORE [[[return faiOrdine(condimenti);]]]
.then(([[[ordine]]]) PASSATO NELL'ARGOMENTO DELLA FUNZIONE SUCCESSIVA
...

fecth

Abbiamo già visto il funzionamento di fecth ed avevamo detto che esso si basa sulle promise; rivediamolo adesso che conosciamo la natura di una promise.

fetch('https://www.cinquepuntozero.it/file.json')

.then(function(response){
    if(response.ok){ 
       return response.json(); }
})

.then(fuction(coversione){
return JSON.parse(conversione);
})

.then(function(oggetto){
console.log(`L oggetto ${oggetto} è stato caricato con successo`;
})

.catch(function(error){
console.log('Non è stato possibile recuperare il file.');
})

Dopo aver conosciuto la struttura di una promise risulta molto più comprensibile il meccanismo di fetch.
Esso è stato sviluppato partendo dal suo predecessore XMLhttpRequest() ed è basato sulle promises.
Il nostro utilizzo invece è basato sulla struttura stessa di promises di fetch.

Promises Custom

E’ possibile mediante il costruttore “new Promise()” creare della promise personalizzate.

var miaPromise = new Promise();

“Promise” deve contenere una funzione, che rappresenta l’azione che deve eseguire la promise.

A livello sintattico la funzione prende il nome di esecutore (Executor) o handler (gestore).

var miaPromise = new Promise(function(resolve, reject) {
...
}

La funzione contenuta all’interno della promise contiene due argomenti
resolve e reject, che in realtà sono due funzioni.
“resolve” rappresenta la funzione da invocare quando la promise viene risolta;
“reject” rappresenta la funzione da invocare quando la promise non può essere risolta.

var miaPromise = new Promise(function(resolve, reject) {
if(condizione){
resolve();
}else{
reject(); }
});

Per comprendere il suo procediamo con un esempio concreto.

function timeOutPromise(messaggio, intervallo){
 
return new Promise(function(resolve,reject){
 
    if(messaggio === ' ' || typeof messaggio != 'string'){
      reject('Il messaggio è vuoto o non è una stringa'); 
    }
    else if(intervallo < 0 || typeof intervallo != 'number'){
      reject('Invtervallo indicato con valore negativo o non si tratta di un numero');
    }else{
     window.setTimeout(function(){
      resolve(messaggio);}, intervallo);
    }
 
});
}

Abbiamo creato una funzione con all’interno una struttura di controllo promise, che assume la gestione degli esiti;
In sostanza stiamo dichiarando una funzione che può restituire più esiti, negativi o positivi, in base a delle regole che imposto nella promise che rappresenta il suo gestore.
Se i dati inseriti saranno accettati restituirà il risultato, altrimenti se i dati inseriti saranno errati restituirà degli errori.

 timeOutPromise("Hello World!", 1500)
.then(function(messaggio){ console.log(messaggio); }
.catch(function(errore){ console.log(errore); }

Creata la nostra funzione con all’interno una promise possiamo procedere con il suo utilizzo; .then() intercetta esito positivo e fa qualcosa con il risultato, .catch() intercetta esito negativo e fa qualcosa con l’errore.

Di fatto non possiamo conoscere a priori l’esito della nostra funzione-con-promise; questo perchè non possiamo sapere se i paramenti passati saranno ben accetti o meno.

I valori all’interno delle funzioni contenuti in .then() e catch() (messaggio e errore) sono passati dalla promise stessa e conterranno o il risultato o l’errore.

Stati delle promises

Nell’esempio precedente abbiamo effettuato un operazione abbastanza basica e di rapida esecuzione; non tutte le operazioni che può effettuare un promise saranno così “leggere” ma in ambito di programmazione asincrona potremo dare in gestione compiti anche più impegnativi a livello di tempistiche di esecuzione.
A tal proposito introduciamo la terminologia usata quando si parla dello stato di una promise.
Una promise può trovarsi in uno dei seguenti stati:

StatoDescrizione
resolved (risolta)Una promise è risolta quando il valore che rappresenta diviene disponibile, cioè quando l’attività asincrona restituisce un valore
rejected (rigettata)Una promise è rigettata quando l’attività asincrona associata non restituisce un valore o perché si è verificata un’eccezione oppure perché il valore restituito non è considerato valido
pending (pendente)Una promise è pendente quando non è né risolta né rigettata, cioè la richiesta di esecuzione di un’attività asincrona è partita ma non abbiamo ancora ricevuto un risultato

Metodi dell’oggetto Promise

Promise.all()

Il metodo Promise.all() è un metodo dell’oggetto Promise che accetta una matrice di promesse (un iterabile) come input.
Restituisce il risultato di una promessa quando tutte le premesse contenute all’interno dell’array sono già risolte o l’array non contiene promesse.

//Due funzioni con promise esterne
recuperaFotoUtente()
recuperaBiografiaUtente()

//Terza funzione con promise
recuperaInfoUtente()
.then(function(utente){ 
//Inserimento delle promise esterne all'interno di un array
var promesseEsterne = [recuperaFotoUtente, recuperaBiografiaUtente];
return Promise.all(promesse) }

.then(function(risultati){
     console.log(risultati[0]);
	 console.log(risultati[1]);
})

.catch(function(errore){
console.log("Si è verificato un errore");
});

La stringa di codice “return Promise.all(promesse)” contenuta all’interno del primo .then() di “recuperaInfoUtente()” restituisce un array con i valori delle promise eseguite.
Questi valori saranno disponibili all’interno del secondo .then() per effetto del chaining; si tratta di un array che questa volta conterrà i risultati delle promises esterne.

In caso di fallimento di una delle promise esterne, la nuova promise sarà rigettata e catturata dal metodo catch(). Se invece entrambe le promise vengono risolte, verrà passata alla funzione di risoluzione della promise un array con i valori risultanti nello stesso ordine con cui le promise corrispondenti sono state inserite nell’array.

Promise.race()
recuperaInfoUtente()
.then(function(utente){ 
var promesseEsterne = [recuperaFotoUtente, recuperaBiografiaUtente];
return Promise.race(promesse) }

.then(function(risultati){
     if(risultati[0] != errore){
     console.log(risultati[0]);}
     }else{
	 console.log(risultati[1]);
})

.catch(function(errore){
console.log("Si è verificato un errore");
});

Il metodo Promise.race() simula di fatto una “corsa”; il funzionamento è del tutto similare a Promice.all() con l’unica differenza che per il proseguo della “recuperaInfoUtente()” promessa è sufficiente che una delle due promesse esterne venga risolta.

Riepilogo

Proviamo a ricapitolare brevemente quanto visto con l’ausilio di pseudo-codice.

Funzione con promise

Una funzione con promise ci consente di gestire un sistema di callback concatenate in maniera semplice ed asincrona, dipendente dalle antecedenti e che prevedono il passaggio di valore tra le stesse.

funzioneConPromise(argomento)
.then(function(risultatoDellaPromise){...})
.then(function(risultatoDelPrimoThen){...})
.then(function(risultatoDelSecondoThen){...})
.catch(function(errore){...})

Costruttore di promise

Il costruttore “new Promise” ci consente di creare promise personalizzabili
dove possiamo gestire gli esiti di una funzione.

function funzioneConPromise(argomento){
return new Promise(function(resolve, reject){
if(argomento condizioneNonRispettata1){
reject("errore 1"); }
else if(argomento condizioneNonRispettata2) {
reject("errore 2"); 
}else{
resolve("risultato");
}
});
}

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *