:orphan: ================================== Programmation Javascript avancée ================================== .. role:: en .. |callback| replace:: `callback`:en: Promesses ========= Rappels sur la programmation asynchrone --------------------------------------- Au `cours précédent `:doc:, 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`:en: -------------- 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`:en: : * 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: 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`:en: passés à ``then`` se "propage" le long de la chaîne, ce qui permet à un ``catch`` 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 ? .. nextslide:: * 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. .. nextslide:: * 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"); .. hint:: 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. .. nextslide:: * Exemple avec ``fetch`` : .. code-block:: javascript 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 : .. code-block:: javascript 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 : .. code-block:: javascript 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 : .. code-block:: 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 : .. code-block:: javascript 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`:lat: un objet, le mot-clé ``this`` dénote cet objet : .. code-block:: javascript 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 : .. code-block:: javascript 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. .. code-block:: javascript 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.`:lat: sans les parenthèses). Ceci est possible en définissant un *accesseur* (`getter`:en: en anglais) : .. code-block:: javascript 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