Programmation Asychrone
Aurélien Tabard, basé sur R. Thion
Semestre printemps 2024-2025 UCBL
<div>
<div> depuis le handler :
this.parentevent.target.parentTrois 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.
💡 Pour 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. ☠️
.then et
.catch renvoient des promesses qui peuvent
être chaînées à leurs tours.Le chainage donne un style linéaire qui évite l’imbrication des traitements (a.k.a., pyramid of doom).
.then()Promise.then(fn) renvoie
toujours une promessefn 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)
.thenfn 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 singlePromisethat 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 / awaitasync / await introduits dans ES2017+ (MDN)async renvoie toujours
une promesse (synchrone) ;await p attend la résolution de la promesse
try, catch et
finallyasync / 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 !
setTimeoutfunction 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.
fetchThe 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)
fetchressource 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 / PromisePromiseasync/awaitreject/resolve dans l’exécuteur