LIFWEB TP4 - Programmation asynchrone en JavaScript
Ce TP utilise l’API fetch du navigateur https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API. Ce TP nécessite un serveur Web de développement, aussi simple soit-il. Vous pouvez utiliser soit :
- L’extension Live Server
- Ouvrir un serveur Python avec le module
http.serveravecpython -m http.server --bind 127.0.0.1 5000(sur le port 5000, enlocalhostseulement).
On donne la page de départ tp4-async.html.
Exercice 0 (PRÉPARATION) : Galerie de chats
Le CM2 sur le DOM, proposait une démonstration avec des chats. La galerie est générée dynamiquement côté client à partir d’un tableau.
Reprendre la galerie, mais au lieu d’utiliser un tableau statique de félins (cats dans le code) utiliser une requête fetch sur https://api.thecatapi.com/v1/images/search?limit=10 pour obtenir la liste et générer la galerie à partir du json obtenu. On devrait obtenir de nouveaux chats à chaque rechargement.
Réaliser la modification en style Promise.then() et en style async/await.
Exercice 1 : requête GET avec fetch
On utilise l’API fetch pour remplir dynamiquement le contenu d’une page. Quand on clique sur le bouton Go download!, effectuer le traitement suivant :
- En JS, télécharger all-bookmarks-status.json avec
fetch. - Lire le contenu du fichier en JSON avec response.json().
- Pour chacun des liens contenus, ajouter un élément de la forme
<li><a href="url_du_lien" target="_blank">titre du lien</a></li>à la liste. - Si le lien est mort (attribut
status), afficher quand même le lien, mais avec un styletext-decoration: line-throughpour qu’il soit rendu barré. Voir la classe CSS.offlinefournie. - Quand on clique sur un lien, demander une confirmation avec
confirm()(MDN) avant de quitter la page.- Intercepter l’événement par défaut pour cela.
- Si le lien est mort, on ne fera rien.
Le rendu final, pour ici une liste de quatre liens dont deux morts, pourrait ressembler à celui-ci :

L’exercice peut être réalisé avec les Promise ou en utilisant async/await, sans promesses explicites. Il est recommandé d’essayer les deux versions.
Exercice 2 : fetch en parallèle ou en série
On donne le code suivant du fichier compare-await-all.js avec différentes façons d’interroger trois sites :
const uris = [
"https://pokeapi.co/api/v2/",
"https://the-site-that-do-no-exists/",
"https://httpbin.org/status/404",
];
const head = (uri) =>
fetch(uri, { method: "HEAD" })
.then((response) => response.status)
.catch((error) => error.message);
(async () => {
async function fetchPromiseAll(uris) {
return await Promise.all(uris.map((uri) => head(uri)));
}
async function fetchForLoopAwait(uris) {
const results = [];
for (const uri of uris) {
results.push(await head(uri));
}
return results;
}
async function fetchMapAwait(uris) {
return uris.map(async (uri) => await head(uri));
}
console.log(await fetchPromiseAll(uris));
console.log(await fetchForLoopAwait(uris));
console.log(await fetchMapAwait(uris));
})();
- Pourquoi utilise-t-on la méthode HTTP
HEAD? - Que renvoie
fetchMapAwait? - Les fonctions
fetchPromiseAlletfetchForLoopAwaitrenvoient le même résultat, mais lesfetchne sont pas résolues dans le même ordre. Utiliser le comma operator(,)(MDN) pour instrumenter la fonctionheadet le prouver en affichant l’ordre de résolution des promesses. - (BONUS) Écrire une nouvelle variante parallèle en utilisant
for ... of(MDN) oufor await ... of(MDN) sur une liste de promesses. - (BONUS) (DIFFICILE) Réécrire une fonction équivalente à
Promise.allen utilisant uniquement des promesses (sansasync/await).
Opérateur virgule (,)
La virgule, e1, e2 en JavaScript évalue le premier argument e1, oublie sa valeur, évalue le second argument e2 et renvoie sa valeur. Le premier argument n’a donc d’intérêt que s’il a des effets de bord, par exemple :
// affiche "echo" juste avant l'affectation de 42 à val
const val = (console.log("echo"), 42);
console.assert(val === 42);
Exercice 3 (BONUS) : soumission de formulaire à serveur tiers
Le but de cet exercice est de gérer ce formulaire côté client, c’est-à-dire qu’on ne va pas envoyer une requête HTTP au serveur d’origine quand on clique sur le bouton Envoyer. À la place, on remplace le comportement par défaut des formulaires par une requête POST à un serveur tiers, ici https://httpbin.org/, et utiliser sa réponse HTTP dans la page, sans passer par le serveur d’origine qui a servi la page HTML.
On veut récupérer les contenus saisis par l’utilisateur dans le formulaire. L’API DOM propose une classe FormData (MDN) pour faciliter la gestion des formulaires. C’est ce que fait la fonction formDataToJSON ci-dessous :
function formDataToJSON(formElt) {
const formData = new FormData(formElt);
return Object.fromEntries(formData.entries());
}
- Avec une requête type
POST, envoyer le contenu du formulaire àhttps://httpbin.org/anything, un exemple est donné après. - Récupérer la réponse HTTP envoyée par le serveur et vérifier que le contenu est bien identique à celui envoyé.
- Tester avec la route
https://httpbin.org/status/501du même site. Traiter l’erreur.
fetch("https://httpbin.org/anything", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
Exercice 4 (AU LONG COURS) : Serveur CTF
Passer les deux premiers niveaux du CTF sur https://lifweb.univ-lyon1.fr/.
