66 Promise.all
analizziamo il metodo all di promise, in modo da poter eseguire diverse promise in parallelo e ottenere il risultato di queste promise memorizzate in un array, oppure se qualcuna di queste promise viene rigettata, anche le altre verranno fermate.
La sintassi è molto semplice, si lancia Promise.all e si passa come parametro un array di promise che devono essere risolte
Promise.all(iterable)
Vediamo un caso pratico utilizzando sempre jsonplaceholder, e prendiamo l’url dei posts
https://jsonplaceholder.typicode.com/posts
concateneremo gli url a seconda del bisogno per il numero del post o i commenti ecco un esempio di url dei commenti per il primo post
https://jsonplaceholder.typicode.com/posts/1/comments
Creiamo una promise per prendere tutti i posts, poi aggiungiamo il post numero 1 e poi un’altra per prelevare i commenti. Noi vogliamo che entrambe le chiamate fatte in parallelo vengano concluse con esito positivo e solo allora processare i dati. Utilizzeremo il metodo fetch. Iniziamo creando una costante contenente l’url
const JSONPLACEHOLDER = 'https://jsonplaceholder.typicode.com/posts';
creiamo una promise con fetch per prelevare il post numero 1, memorizzandola in una variabile che chiameremo post, aggiungiamo un dollaro finale al nome della variabile ( $ ) che è una convenzione di uso comune per indicare che tale variabile contiene una promise o nel caso degli osservarle è un osservable, non un valore ancora risolto
let post$ = fetch(JSONPLACEHOLDER + '/1')
Ricordiamoci che questa chiamata di fetch ritorna una response, la quale al suo interno contiene il JSON nel body di cui vogliamo catturarne i dati, quindi concatenando il metodo then che avrà una risposta di tipo response di cui ci faremo restituire il suo metodo json() (della response), assegneremo la promise restituita alla variabile post$
.then(resp => resp.json() );
In modo identico creiamo la seconda chiamata che restituirà la promise per i commenti
let comments$ = fetch(JSONPLACEHOLDER + '/1/comments')
.then(
resp => resp.json()
);
Ora andiamo a testare semplicemente le chiamate con
post$.then()
in modo da risolvere la promise contenuta in post$. Passiamo a then l’elenco dei post ricevuti usando la funzione freccia che passerà il post in console
post$.then( post => console.log(post));
stessa cosa per i commenti
comments$.then( comments => console.log(comments));
Controllando la console vedremo le 2 chiamate funzionanti con la restituzione dei valori, per la prima chiamata i dati del post 1 , per la seconda i commenti del post numero 1

Andando nella sezione Network dell’inspector del browser e selezionando il tab XHR vedremo le 2 chiamate AJAX refreshando la pagina. Cliccando su ogni chiamata vedremo i dati restituiti in formato JSON.
Adesso che abbiamo appurato che le chiamate funzionano, commentiamo i 2 test e andiamo ad utilizzare Promise.all che riceverà un array contenente le 2 variabili che hanno le 2 promise
Promise.all([post$, comments$])
Infine il then avrà come risposta l’array con le promise soddisfatte entrambe, indipendentemente da quale promise parte prima o dopo
.then(
resp => {
console.log(resp);
}
)
In console riceverò ora l’array di risposta contenente le 2 promise fetch risolte, ecco il codice completo
const JSONPLACEHOLDER = 'https://jsonplaceholder.typicode.com/posts';
let post$ = fetch(JSONPLACEHOLDER + '/1')
.then(
resp => resp.json()
);
//post$.then( post => console.log(post));
let comments$ = fetch(JSONPLACEHOLDER + '/1/comments').then(
resp => resp.json()
);
//comments$.then( comments => console.log(comments));
Promise.all([post$, comments$])
.then(
resp => {
console.log(resp);
}
)
Ciclare i dati
Ora possiamo inserire i dati nell’ HTML,
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Promise all</title>
<script src="fetchall.js"></script>
</head>
<body>
</body>
</html>
creiamo un <div> contenente un <h1> con id postTitle e un <div> con id postBody e una lista con id comments
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Promises</title>
<script src="fetchall.js"></script>
</head>
<body>
<div>>
<h1 id="postTitle"></h1>
<div id="postBody"></div>
</div>
<ul id="comments">
</ul>
</body>
</html>
nel metodo then del Promise.all nel file javascript eliminiamo la verifica in console e creiamo una variabile post che contiene l’indice zero dell’array (cioè contiene i dati del nostro post)
Promise.all([post$, comments$])
.then(
resp => {
let post = resp[0];
}
)
ora andiamo ad inserire col querySelector nell’h1
document.querySelector('#postTitle')
aggiungiamo l’HTML con innerHTML le chiavi body e title recuperate dall’inspector del browser
let post = resp[0];
document.querySelector('#postTitle').innerHTML = post.title;
document.querySelector('#postTitle').innerHTML = post.body;
Per i commenti che si trovano alla posizione 1 dell’array Promise.all, utilizziamo un ciclo foreach
let comments = resp[1];
comments.forEach( comment => {
let li = document.createElement('li');
})
aggiungiamo la chiave body tratta dal valore 1 dell’array vista dall’inspector
li.innerHTML = comment.body;
aggiungiamo in testa una variabile ul che seleziona l’ul del nostro HTML
let ul = document.querySelector('#comments');
adesso con ul effettuiamo un appendChild passandogli li. Abbiamo inserito la descrizione e i commenti nel file HTML.
Ecco il codice js completo
const JSONPLACEHOLDER = 'https://jsonplaceholder.typicode.com/posts';
let post$ = fetch(JSONPLACEHOLDER + '/1')
.then(
resp => resp.json()
);
let comments$ = fetch(JSONPLACEHOLDER + '/1/comments').then(
resp => resp.json()
);
Promise.all([post$, comments$])
.then(
resp => {
let post = resp[0];
document.querySelector('#postTitle').innerHTML = post.title;
document.querySelector('#postBody').innerHTML = post.body;
let comments = resp[1];
let ul = document.querySelector('#comments');
comments.forEach(comment => {
let li = document.createElement('li');
li.innerHTML = comment.body;
ul.appendChild(li);
})
}
)
Ovviamente poi si dovrebbe sistemate col CSS per dare un aspetto gradevole, ma abbiamo visto come generare dinamicamente una pagina web estraendo i dati con API e chiamate AJAX con Promise fetch e Promise.all. Qualora ci fosse qualche errore sollevato anche da 1 delle promise, dobbiamo gestirlo concatenando l’eccezione catch in fondo
).catch(err =>console.log(err));
Solleviamo l’eccezione throw nel primo then dei commenti, commentando la restituzione del json
let post$ = fetch(JSONPLACEHOLDER + '/1')
.then(
resp => { throw new Error('Errore contattando il server')}//resp.json()
);
In questo modo le promise non verranno risolte ed abbiamo simulato un errore del server che ci verrà restituito refrashando la pagina.
Concludendo, la promise.all è molto utile quando vogliamo effettuare più chiamate contemporaneamente e vogliamo processare le risposte solo quando tutte sono risolte.
Un esempio pratico sono una chiamata per ottenere l’elenco delle regioni, un altra per le provincie e una per le città, ci interessa processare i risultati quando tutte le chiamate sono andate a buon fine.