Programmation Asychrone
Romuald THION
Semestre printemps 2023-2024 UCBL
<div>
<div>
depuis le handler :
this.parent
event.target.parent
Solution du TP avec
MutationObserver
(BONUS 1) et <template>
(BONUS 2) (fichiers galerie.html, galerie.js et galerie.css).
<div>
<div>
autour des boutonsTrois grandes approches pour les traitements asynchrones :
async/await
: simplification sur les promesses.💡 Pour comprendre les promesses, il faut comprendre les callbaks et le style CPS.
💡 Pur comprendre async/await
il faut comprendre les
promesses.
error
: vaut null
en cas de succès.value
ou data
: valeur de
retour.L’interface Promise
représente un intermédiaire
(proxy) vers une valeur qui n’est pas nécessairement connue au
moment de sa création. Ainsi, des méthodes asynchrones
renvoient des valeurs comme les méthodes synchrones, la seule
différence est que la valeur retournée par la méthode asynchrone est une
promesse (d’avoir une valeur plus tard).
Promise
introduit en ES2015+ (MDN)asyncFunc(...params, cb)
Promise
renvoyé par la fonction :
asyncFunc(...params).then(cb)
🤯 Pour créer une Promise
, on donne une fonction
d’ordre supérieure qui elle-même prend deux
fonctions en paramètres, nommées (par convention) :
resolve
qui va résoudre en succès ;reject
qui va résoudre en échec.const promise = new Promise(function (resolve, reject) {
if (condition) {
resolve(value); // ✅ succès
} else {
reject(reason); // ❎ échec
}
});
💡 Il est assez rare de créer directement des promesses via le constructeur.
⚠️ L’environnement fixe resolve
et
reject
(code natif) et appelle l’exécuteur
immédiatement (exemple).
console.log("A");
const promise = new Promise(function (resolve, reject) {
console.info(`resolve = ${resolve}`);
console.info(`reject = ${reject}`);
resolve(42);
});
console.log(promise);
console.log("B");
promise.then(console.log);
console.log("C");
☠️ C’est assez piégeux. Voir l’exemple. ☠️
Le chainage donne un style linéaire qui évite l’imbrication des traitements (a.k.a., pyramid of doom).
.then()
fn
retourne une promesse
R
dont le résultat sera utilisé quand elle sera résolue
;fn
retourne une valeur
R
qui sera transformée automatiquement en une
promesse immédiatement résolue..then()
, .catch()
et
.finally()
.then()
, .catch()
et
.finally()
console.info("Start");
Promise.resolve(1)
.then(inc)
.then((_) => Promise.reject(new Error("Broken")))
.then(inc) // ⚠️ n'est pas exécuté
.catch((err) => {
console.error(err);
return 42;
}) // ⚠️ le traitement normal reprend
.then(inc)
.finally(() => console.log("Done"));
console.info("End");
❓ Qu’est-ce qui s’affiche ? (exemple)
.then
fn
n’est pas une fonction
:
.then
;Promise.resolve(42).then(1).then(console.log);
// 🪳 affiche 42 car 1 n'est **pas** une fonction
const p = Promise.resolve(42);
p.then(() => p.then(() => console.log(0)));
// 💡 affichera bien 0, même si 42 était déjà résolue
⚠️ Ici pour l’exemple, on a imbriqué les promesses, mais c’est
à éviter, voir promise/no-nesting
.
⚠️
🤯 La promisification consiste à transformer une API par callback en une API qui renvoie une Promise.
function promisify(funct) {
// 💡 wrapper (*) returns a Promise
return function (...args) {
return new Promise((resolve, reject) => {
// custom callback/executor (**)
function callback(err, result) {
if (err) reject(err); // ❎
else resolve(result); // ✅
}
funct.call(this, ...args, callback);
// append custom callback and call the original function
});
};
}
Voir exemple.
setTimeout
// API callback traditionnelle
const waitCallback = (delay, value, cb) => setTimeout(() => cb(null, value), delay);
const logCallback = (err, res) => (err ? console.error(err) : console.log("waitCallback", res));
waitCallback(1000, 42, logCallback);
// Promisification manuelle
const waitPromise = promisify(waitCallback);
waitPromise(1000, 42)
.then((v) => console.log("waitPromise", v))
.catch(console.error);
Voir exemple.
La bibliothèque standard Node.js propose les outils util.promisify
et util.callbackify
.
// Promisification via promisify standard
const waitPromiseNode = nodePromisify(waitCallback);
waitPromiseNode(1000, 42)
.then((v) => console.log("waitPromiseNode", v))
.catch(console.error);
// Via nouvelle API Promisifiée de setTimeout
const waitNative = (delay, value) => setTimeoutPromise(delay, value);
waitNative(1000, 42)
.then((v) => console.log("waitNative", v))
.catch(console.error);
⚠️ Si vous devez promisifier, use the platform. ⚠️ Voir exemple.
Promise
💡 Méthodes de classe, pas d’instances
The
Promise.all()
method returns a singlePromise
that resolves when all of the promises in the iterable argument have resolved.The
Promise.allSettled()
method returns a promise that fulfills after all of the given promises have either fulfilled or rejected.
Promise.all([
new Promise((resolve) => setTimeout(() => resolve(1), 0)),
new Promise((resolve) => setTimeout(() => resolve(2), 1000)),
new Promise((resolve) => setTimeout(() => resolve(3), 500)),
]).then(console.info);
💡 Affiche [1, 2, 3]
après une seconde et
garanti l’ordre dans le tableau. Voir l’exemple.
Promise.any()
takes an iterable of Promise objects. It returns a single promise that fulfills as soon as any of the promises in the iterable fulfills, with the value of the fulfilled promise.The
Promise.race()
method returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects
De façon simplifiée, fonctionnellement équivalent :
async
/ await
async
/ await
introduits dans ES2017+ (MDN)async
renvoie toujours
une promesse (synchrone) ;await p
attend la résolution de la promesse
try
, catch
et
finally
async
/ await
💡 await
correspond moralement à une ré-écriture du
traitement dans le then
des Promise
.
❗ Pour maîtriser async
/ await
, il
faut maîtriser les Promise
!
setTimeout
function timeout(ms) {
return new Promise((resolve) => setTimeout(() => resolve("Get Up!"), ms));
}
async function sleep() {
const r = await timeout(3000);
console.info(r);
return r;
}
console.log("Before");
await sleep(); // ⚠️ await SUSPEND l'EXECUTION ⚠️
console.log("After");
⚠️ Ne pas mettre des await
au 🎲 ! Voir
l’exemple.
async/await
)import { readFile } from "node:fs/promises";
const mypath = "async-download.html";
try {
const data = await readFile(mypath, { encoding: "utf8" });
console.log(">>>");
console.log(data);
console.log("<<<");
} catch (error) {
console.error(error);
}
👍 Respecte le guide de style. Voir exemple.
fetch
The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest, but the new API provides a more powerful and flexible. (MDN)
fetch
ressource
de type string
,
URL
ou Request
.POST
, PUT
ou
PATCH
, le body est passé dans les options, avec le
type de méthode, l’entête, etc.async
/ Promise
Promise
async
/await
reject
/resolve
dans l’exécuteur