Programmation Javascript avancée¶§
Promesses¶§
Rappels sur la programmation asynchrone¶§
Au cours précédent, on a vu qu’en programmation asynchrone, il était fréquent de passer une fonction en paramètre d’une autre fonction, afin que la première soit appelée plus tard.
Exemple 1 :
b.addEventListener('click',
(evt) => { console.log("Vous avez cliqué"); }
);
Exemple 2 :
setTimeout(
() => { console.log("Une seconde s'est écoulée"); }
, 1000);
Exemple 3 :
fetch("http://example.org/").then(
(resp) => { console.log("Le serveur a répondu"); }
).catch(
(err) => { console.log("Le serveur ne répond pas"); }
);
Callback¶§
Historiquement, la fonction a appeler plus tard est passée directement en paramètre de la fonction appelante (exemples 1 et 2 ci-avant). On appelle la fonction passée en paramètre un callback.
Parfois, une fonction requiert deux callbacks :
- un à appeler en cas de succès,
- et un à appeler en cas d’erreur.
Les fonctions plus récentes n’attendent pas de callback en paramètre, mais retournent un objet Promesse.
Promesse¶§
- Une promesse est un objet représentant un traitement asynchrone.
- Elle possède trois méthodes principales,
qui attendent toutes un callback en paramètre :
then
: callback à exécuter en cas de succès ;catch
: callback a exécuter en cas d’erreur ;finally
: callback à exécuter dans tous les cas.
- Ces méthodes retournent à leur tour une promesse (encapsulant la valeur retournée par le callback) ce qui permet d’en enchaîner plusieurs.
- Une erreur dans l’un des callbacks passés à
then
se “propage” le long de la chaîne, ce qui permet à uncatch
de ratrapper toutes les erreurs produites au dessus de lui.
Avantage : lisibilité accrue¶§
Exemple avec des callback anonymes :
setTimeout(() => { console.log("un"); setTimeout(() => { console.log("deux"); setTimeout(() => { console.log("trois"); }, 3000); }, 1000); }, 2000); console.log("coucou");
Note
À votre avis, dans quel ordre s’affiche le texte, et avec quel délais ?
Alternative avec des fonctions nommées en callback :
setTimeout(étape1, 2000); function étape1() { console.log("un"); setTimeout(étape2, 1000); } function étape2() { console.log("deux"); setTimeout(étape3, 3000); } function étape3() { console.log("trois"); }
Note
Cette version est plus lisible, mais verbeuse, et oblige à donner un nom à chaque étape.
Exemple avec une hypothétique fonction
sleep
, qui prendrait en paramètre un délais et retournerait une promesse :sleep(2000) .then(() => { console.log("one"); return sleep(1000); }).then(() => { console.log("two"); return sleep(3000); }).then(() => { console.log("three"); }); console.log("coucou");
Indice
Attention de ne pas oublier le return
à la fin de chaque
callback passé à then
,
sans quoi il retournera null
,
et le then
suivant s’exécutera immédiatement.
Exemple avec
fetch
:fetch('http://example.org') .then((response) => { // cette fonction est appelée lorsque // les en-têtes de la réponse ont été reçu if (!response.ok) { throw ("Error " + response.status); } return response.json() // attend la contenu }).then((data) => { // cette fonction est appelée lorsque // le contenu de la réponse a été reçu, // et analysé comme du JSON do_something_with(data); }).catch((err) => { // cette fonction est appelée en cas d'erreur console.log(err); });
Avantage : combinaison de promesses¶§
Attendre que toutes les promesses d’un tableau aient abouti :
Promise.all( [fetch(url1), fetch(url2), fetch(url3)] ).then( (responses) => { console.log("toutes les URLs ont répondu"); } ).catch( (err) => { console.log("une erreur s'est produite", err); } )
Attendre qu’au moins une des promesses d’un tableau ait abouti :
Promise.race( [fetch(url1), fetch(url2), fetch(url3)] ).then( (response) => { console.log("une des URL a répondu")} )
Programmation orientée-objet¶§
Méthodes d’objet¶§
On a vu précédemment comment créer un objet en Javascript :
let v = { x: 3, y: 4, }; console.log(v.x, v.y);
On a également vu qu’une fonction anonyme peut être affectée à une variable, comme n’importe quelle autre valeur :
let x = { greet: function(name) { console.log("hello", name)} } x.greet("world"); // affiche "hello world"
Le mot-clé this
¶§
En Javascript,
lorsqu’on accède à une fonction via un objet,
le mot-clé this
dénote cet objet :
let v = {
x: 3.0,
y: 4.0,
length: function() {
return Math.hypot(this.x, this.y);
}
}
v.length(); // retourne 5
Note
Les fonctions “flêches” font exception à cette règle,
c’est pourquoi on utilise le mot-clé function
ici.
Méthodes ES6¶§
En ES6, on peut également utiliser la syntaxe raccourcie suivante :
let v = {
x: 3.0,
y: 4.0,
length() {
return Math.hypot(this.x, this.y);
}
}
v.length(); // retourne 5
Classes ES6¶§
En ES6, il est possible de définir une classe, pour créer plusieurs objets ayant la même structure et les mêmes méthodes.
class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
length() {
return Math.hypot(this.x, this.y);
}
}
let v = new Vector(3, 4);
v.length(); // retourne 5
Note
constructor
est une méthode particulière,
appelée lorsqu’on utilise l’opérateur new
.
Attributs contrôlés par des méthodes¶§
Dans l’exemple précédent,
on pourrait souhaiter utiliser length
comme un attribut (en lecture seule) plutôt que comme une méthode
(i.e. sans les parenthèses).
Ceci est possible en définissant un accesseur (getter en anglais) :
class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
get length() {
return Math.hypot(this.x, this.y);
}
}
let v = new Vector(3, 4);
v.length; // retourne 5