La programmation asynchrone moderne en JavaScript
Romuald THION
Semestre pair 2022 UNC
return
ne permet pas de renvoyer de valeur
throw
dans un cas de succèssetTimeout
function throttle(funct, delay) {
let waiting = false;
return function (...args) {
if (!waiting) {
waiting = true;
setTimeout(() => {
waiting = false;
}, delay);
return funct(...args);
}
};
}
Le décorateur throttle, une variante de memo
avec remise à zéro par une alarme.
Ou comment éviter la pyramid of doom
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).
Ce qui change : au lieu de passer le callback en paramètre de la fonction asynchrone, on va passer le callback à l’objet Promise
renvoyé par la fonction.
Promise
Pour créer un Promise
, on doit passer une fonction d’ordre supérieure qui elle-même prend deux fonctions en paramètre, nommées par convention :
resolve
qui va résoudre en succès (fulfilled)reject
qui va résoudre en échec (rejected)Attention l’environnement fixe resolve
et reject
(code natif) et appelle l’exécuteur immédiatement (exemple)
Les méthodes then(callback)
et catch(callback)
renvoient des promesses qui peuvent être chaînées à leurs tours.
Le chainage donne un style linéaire qui évite une imbrication des traitements (la pyramid of doom)
Promise.prototype.then(fn)
renvoie une promesse :
fn
retourne une promesse R
dont le résultat sera utilisé quand elle sera résolu ,fn
retourne une valeur R
qui sera transformée automatiquement en une promesse immédiatement résolue.Promise
Promise
: un exempleconsole.info("Start");
Promise.resolve(1)
.then(inc)
.then((_) => Promise.reject(new Error("Broken")))
.then(inc) // /!\ sauté /!\
.catch((err) => {
console.error(err);
return 42;
}) // /!\ attention au retour /!\
.then(inc)
.finally(() => console.log("Done"));
console.info("End");
Qu’est-ce qui s’affiche ? (source)
then
fn
n’est pas une fonction :
Promise.resolve(42).then(1).then(console.log)
42
et pas 1
then
qui sera déclenché au prochain tour
const p = Promise.resolve(42);
p.then(() => p.then(() => console.log(0)));
0
setTimeout
manuellefunction timedValue(value, delay) {
function executor(resolve, reject) {
setTimeout(() => resolve(value), delay);
}
return new Promise(executor);
}
const p = timedValue(1000, "Success!");
// on enchaine ici 3 callbacks sans imbrication
p.then((str) => "OK! " + str)
.then((str) => str + " Done!")
.then((str) => console.log(str));
La promisification consiste à transformer une API par callback en une API qui renvoie une Promise.
function promisify(funct) {
// wrapper (*)
return function (...args) {
return new Promise((resolve, reject) => {
// custom callback/executor (**)
function callback(err, result) {
if (err) reject(err);
else resolve(result);
}
args.push(callback); // append custom callback
funct.call(this, ...args); // call the original function
});
};
}
// avec API callback
const wait = (delay, value, cb) => setTimeout(() => cb(null, value), delay);
wait(1000, 42, (err, res) => (err ? console.error(err) : console.log(res)));
// avec Promisification
const waitPromise = promisify(wait);
waitPromise(1000, 42).then(console.log).catch(console.error);
La bilbiothèque standard Node.js propose les outils util.promisify
et util.callbackify
.
Promise
Des 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.
Exemple, lancement de trois promesses en parallèle:
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
Méthodes de classe pour des promesses immédiatement résolues ou rejetées
reject
/resolve
dans l’exécuteur
fetch
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
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
fetch
string
, une URL
ou une Request
POST
, PUT
ou PATCH
, le body est passé dans les options, avec le type de méthodepython -m http.server
ou LiveServer dans le dossier de travailasync-download.js
pour que le contenu du fichier dont le nom est passé dans l’input soit affiché dans la balise code
async
et await
async
/ await
Sont là pour faciliter l’utilisation des promesses :
async
/ await
en Promise
Les await
sont à peu près équivalents à une ré-écriture de code 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");
Attention à ne pas mettre des await
de partout !
async
/ Promise
Exemple Google Web Fundamentals
async-download.js
en utilisant async
/await