Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 94 di 112
  • livello avanzato
Indice lezioni

Promise native

Secondo specificha ECMAScript 2015, le promise sono integrate nativamente in Javascript, vediamo i dettagli degli stati e le funzioni Then e Catch.
Secondo specificha ECMAScript 2015, le promise sono integrate nativamente in Javascript, vediamo i dettagli degli stati e le funzioni Then e Catch.
Link copiato negli appunti

Fino a qualche anno fa, le promise nelle applicazioni JavaScript venivano utilizzate sfruttando alcune librerie che implementavano il Promise Pattern. A partire dalle specifiche di ECMAScript 2015, le promise sono diventate un componente nativo del linguaggio. Questa importante novità offre sia il vantaggio di non dipendere da librerie esterne, sia la comodità di stabilire un'API standard per la gestione di codice asincrono basato sul Promise Pattern.

Gli stati delle promise

Prima di entrare nel dettaglio dell'utilizzo delle promise native in JavaScript, definiamo la terminologia usata quando si parla del loro stato. Una promise può trovarsi in uno dei seguenti stati:

Stato Descrizione
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é risoltarigettata, cioè la richiesta di esecuzione di un'attività asincrona è partita ma non abbiamo ancora ricevuto un risultato

È importante tener presente che una promise può passare dallo stato pendentÈe ad uno solo degli altri due stati. Non sono previsti altri passaggi di stato, quindi quando una promise passa in stato rigettato o risolto, non può più cambiare stato.

Creare una promise

Una volta introdotta la terminologia di base, vediamo come creare una promise. La sintassi generale è analoga a quella mostrata di seguito:

var promise = new Promise(handler);

Il costruttore Promise() prevede un parametro rappresentato da una funzione (promise handler) che ha il compito di gestire la risoluzione o il rigetto della promisestessa. In generale, un promise handler presenta la seguente struttura di massima:

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

Come possiamo vedere, il promise handler riceve due funzioni come parametri.

Il primo parametro rappresenta la funzione da invocare quando il valore restituito dall'operazione asincrona è disponibile. Il valore restituito dalla attività asincrona viene passato a tale funzione.

Il secondo parametro è la funzione da invocare quando la promise non può essere risolta, ad esempio perchè si è verificato un errore o perché il valore restituito non è considerato valido. In questo caso alla funzione viene passato il motivo del rigetto, come può essere ad esempio l'eccezione che si è verificata.

Promise e Ajax

Vediamo come creare una promise prendendo in esame un caso concreto. Sappiamo già che le le chiamate verso il server sono asincrone e sappiamo come gestirle sfruttando l'oggetto XMLHttpRequest. Vediamo come semplificare l'interazione con il server inserendo una gestione con promise. Una possibile soluzione per una chiamata di tipo GET è mostrata dal seguente codice:

function httpGet(url) {
	return new Promise(function(resolve, reject) {
		var httpReq = new XMLHttpRequest();
		httpReq.onreadystatechange = function() {
			var data;
			if (httpReq.readyState == 4) {
				if (httpReq.status == 200) {
					data = JSON.parse(httpReq.responseText);
					resolve(data);
				} else {
					reject(new Error(httpReq.statusText));
				}
			}
		};
		httpReq.open("GET", url, true);
		httpReq.send();
	});
}

In questo caso abbiamo definito una funzione httpGet() che prende in input l'url della risorsa da richiedere al server e restituisce una promise. Il promise handler effettua la chiamata asincrona verso il server tramite XMLHttpRequest e stabilisce se risolvere o meno la promise in base alla risposta ricevuta. In caso di un codice di stato HTTP 200 la promise viene risolta passando alla funzione di risoluzione il JSON ottentuto dal body della risposta HTTP. In caso contrario la promise viene rigettata passando alla funzione di rigetto un'eccezione con il motivo del fallimento.

Then, utilizzare le promise

A questo punto possiamo utilizzare la funzione httpGet() per inviare richieste al server sfruttando la comodità delle promise. Vediamo come utilizzarla.

Il seguente esempio mostra come ottenere la promise associata ad una specifica chiamata HTTP:

var myPromise = httpGet("/utente/123");

Una volta assengata la promise a una variabile, possiamo elaborare il risultato asincrono che verrà restituito tramite il metodo then(), come possiamo vedere di seguito:

myPromise.then(
	function(utente) {
		console.log("Il server ha restituito l'utente " + utente.nome);
	},
	function(error) {
		console.log("Si è verificato l'errore " + error.message);
	}
);

Il metodo then() prende due parametri: il primo parametro è la funzione che verrà eseguita nel caso in cui la promise venga risolta; il secondo parametro è la funzione che verrà eseguita se la promise viene rigettata. Queste due funzioni corrispondono in pratica alle due funzioni passate come parametri al promise handler.

Naturalmente non è necessario utilizzare una variabile per gestire il risultato di una promise. Il seguente codice è equivalente a quello precedente:

httpGet("/utente/123").then(
	function(utente) {
		console.log("Il server ha restituito l'utente " + utente.nome);
	},
	function(error) {
		console.log("Si è verificato l'errore " + error.message);
	}
);

Anche se è possibile passare al metodo then() un solo parametro, la funzione di risoluzione della promise, in linea di massima è sconsigliato farlo. Infatti, se non specifichiamo un gestore del rigetto della promise come secondo parametro, in caso di errore non avremmo nessun feedback.

Supponiamo di voler mostrare l'elenco dei post del blog di un utente. Possiamo scrivere un brano di codice analogo al seguente:

httpGet("/utente/123")
.then(function(utente) {
	httpGet("/blog/" + utente.blogId)
	.then(function(blog) {
		displayPostList(blog.posts);
	});
});

In questo caso, la funzione che gestisce la risoluzione della promise associata al recupero dell'utente utilizza nuovamente la funzione httpGet() per ottenere una promise associata al recupero del blog dell'utente. La risoluzione di quest'ultima promise consente di visualizzare l'elenco dei post dell'utente.

Concatenare le promise e migliorare la leggibilità

Il codice appena mostrato non eccelle in leggibilità. Diciamo che è del tutto analogo alla sequenza di callback a cui le promise vorrebbero porre rimedio. In realtà possiamo riscrivere il codice precedente in maniera più leggibile sfruttando il fatto che il metodo then() restituisce sempre una promise e possiamo pertanto concatenare il tutto come mostrato di seguito:

function getUtente() {
	return httpGet("/utente/123");
}
function getBlog(utente) {
	return httpGet("/blog/" + utente.blogId);
}
function displayBlog(blog) {
	displayPostList(blog.posts);
}
getUtente()
.then(getBlog)
.then(displayBlog);

Abbiamo innanzitutto definito delle funzioni per rendere più esplicite le attività eseguite. Quindi abbiamo concatenato le promise restituite dai singoli passi che recuperano l'utente ed il suo blog.

catch()

Nell'esempio precedente abbiamo utilizzato il metodo then() senza specificare la funzione per la gestione del rigetto della promise. Come abbiamo detto prima, questa è una pratica da evitare altrimenti rischiamo di non catturare eventuali malfunzionamenti e di non renderci conto di cosa sta succedendo. Possiamo ad esempio definire una semplice funzione che si limita a visualizzare sulla console il messaggio d'errore:

function gestisciErrore(error) {
	console.log(error.message);
}

ed utilizzarla per gestire il rigetto delle promise:

getUtente()
.then(getBlog, gestisciErrore)
.then(displayBlog, gestisciErrore);

In questo modo se per un qualsiasi motivo la promise dovesse essere rigettata, vedremo il messaggio d'errore sulla console. Naturalmente questo è solo un esempio. In applicazioni reali può essere necessaria una gestione degli errori più sofisticata. Inoltre, nel nostro esempio abbiamo utilizzato lo stesso gestore di errore per tutte le promise, mentre si potrebbe aver necessità di un gestore specifico per ciascuna promise. Sono comunque scelte che vanno prese in base alla specifica esigenza.

Se è sufficiente avere un solo gestore del rigetto delle promise, come nel nostro esempio, possiamo evitare di passare lo stesso gestore per ciascun metodo then() e utilizzare il metodo catch(), come mostrato di seguito:

getUtente()
.then(getBlog)
.then(displayBlog)
.catch(gestisciErrore);

Con questo approccio abbiamo centralizzato la gestione del rigetto della promise garantendo allo stesso tempo robustezza e leggibilità.

Ti consigliamo anche