Programmation asynchrone¶§
Fonction anonyme¶§
Objets de premier niveau¶§
- En Javascript, les fonctions sont des objets de premier niveau, ce qui signifie qu’ils peuvent être manipulés au même titre que, par exemple, les entiers, les tableaux ou les objets.
- On peut par exemple
- affecter une fonction à une variable ;
- la passer en paramètre d’une autre fonction
(par exemple
addEventListener
) ; - l’affecter comme attribut à un objet (ce qui est une manière de doter un objet de méthodes, comme en Python).
Fonctions anonymes¶§
Javascript autorise la création de fonctions anonymes :
function (param1, param2) { /* instructions here */ }
Contrairement à une déclaration de fonction, une fonction anonyme peut apparaître partout ou une fonction est acceptée.
C’est utile dans les cas ou cette fonction n’a pas vocation à être réutilisée ailleurs, en particulier pour les abonnements à des événements.
Exemple :
let b = document.querySelector("button");
b.addEventListener('click', function() {
let i = document.getElementsByTagName("input")[0];
i.value = Number(i.value) + 1;
});
Fonctions “flêches”¶§
En ES6, il existe une syntaxe plus compacte pour les fonctions anonymes. Au lieu d’écrire :
function (x, y) { return x+y; }
on peut utiliser la notation :
(x, y) => { return x+y; }
Dans le cas où la fonction comporte une unique instruction return
,
on peut même remplacer le corps de la fonction par l’expression à retourner :
(x, y) => x+y
Dans le cas où la fonction comporte exactement un argument, on peut ommettre les parenthèses autour de l’argument :
x => { console.log(x); return x+1; }
Autres exemples :
x => x+1
() => {
console.log("Fonction flêche sans argument,");
console.log("et comportant plusieurs instructions.");
}
Adaptation de l’exemple précédent :
let b = document.querySelector("button");
b.addEventListener('click', () => {
let i = document.getElementsByTagName("input")[0];
i.value = Number(i.value) + 1;
});
Note
Les fonctions “flêches” ne sont pas absolument équivalentes aux fonctions anonymes, mais la distinction concerne des notions non abordées dans ce chapitre.
Pour en savoir plus : https://stackoverflow.com/a/34361380
Modularité grâce aux fonctions anonymes¶§
Les variables déclarées hors de toute fonction,
même préfixées par let
,
sont considérées par Javascript comme des variables globales.
Afin de contourner ce problème, il existe en Javascript une convention : on “enferme” tout le code du script dans une fonction anonyme, que l’on appelle immédiatement :
(function() {
// ... mon code Javascript ici ...
})();
Ainsi, on évite de polluer l’environnement global.
Autre exemple d’utilisation de fonctions anonymes¶§
let b1 = document.querySelector('button#b1');
b1.addEventListener("click", () => {
let previousContent = b1.textContent;
b1.textContent = "(en attente)";
b1.disabled = true;
setTimeout(() => {
alert("message");
b1.textContent = previousContent;
b1.disabled = false;
}, 2000);
});
Exécution asynchrone¶§
Motivation¶§
Dans l’exemple précédent,
pourquoi utiliser la fonction setTimeout
et non une fonction qui bloquerait l’exécution pendant 2s,
comme on le ferait (par exemple) en Python,
à l’image de l’exemple ci-dessous ?
// ⚠ MAUVAIS EXEMPLE ⚠
let previousContent = b1.textContent;
b1.textContent = "(en attente)";
b1.disabled = true;
sleep(2000); // fonction imaginaire (n'existe pas en JS)
alert("message");
b1.textContent = previousContent;
b1.disabled = false;
Note
La fonction sleep
n’existe pas réellement en Javascript,
et pour cause puisque ce n’est pas la bonne manière de faire.
- Réponse : parce que le navigateur ne peut pas faire deux choses à la fois dans une page : gérer l’affichage du HTML et exécuter le code Javascript.
- Pendant qu’une fonction Javascript s’exécute, la page est totalement “gelée” (saisie, définelement...).
- Si la fonction garde la main pendant une durée trop longue, ce phénomène sera perceptible, et dégradera l’expérience utilisateur.
Portée et fermeture¶§
Portée (scope)¶§
- La portée d’une variable est la partie du code sur laquelle cette variable est définie.
- Pour les variables globales, c’est l’ensemble des scripts exécutés par la page (y compris les scripts écrits par d’autres).
- Pour une variable locale (
let
), c’est le bloc entre accolades dans lequel elle est déclarée. - Une variable locale d’une fonction
f
est donc accessible par les fonctions (anonymes ou non) définies à l’intérieur def
.
Fermeture (closure)¶§
- Une fonction définie à l’intérieur d’une autre porte avec elle le contexte dans lequel elle a été créé, c’est à dire l’état des variables locales qui lui sont accessibles.
- Une telle fonction assortie d’un contexte est appelée une fermeture.
- Dans l’exemple précédent,
la variable
b1
est définie au chargement du script, mais « survit » à cette fonction, puisqu’elle est réutilisée au clic sur ce bouton, et encore deux secondes plus tard, dans le callback de la fonctionsetTimeout
. - Visualisez l’exécution pas à pas d’un autre exemple de fermeture sur pythontutor.
Gestion avancée d’événements¶§
Cheminement d’un événement¶§
- La plupart des événements se propagent dans l’arbre DOM.
- Il est donc possible de s’abonner à tous les événements se produisant à l’intérieur d’un élément (et pas uniquement sur cet élément).
- Par défaut, les listeners dont déclenchés à la remontée de l’événement.
Note
On peut également forcer un listener à se déclencher à la descente
(capture d’un événement) plutôt qu’à sa remontée (bubbling).
Pour cela on passera true
en troisème paramètre de
addEventListener.
Paramètre d’un listener¶§
- Le listener reçoit en paramètre un objet
événement,
contenant notamment les attributs suivants :
target
: l’élément le plus spécifique concerné par l’événement ;type
: le type d’événement (click
,mouseover
...)
document.getElementsByTagName("body")[0]
.addEventListener("click", (evt) => {
let msg = document.getElementById("msg");
if (evt.target.tagName === "BUTTON") {
msg.textContent = "Vous avez cliqué sur " +
evt.target.textContent;
} else {
console.log(evt.target);
msg.textContent = "Vous avez raté les boutons...";
}
});
AJAX¶§
Définition¶§
AJAX signifie
- Asynchronous
- Javascript
- And
- XML
... mais en pratique, on peut échanger n’importe quoi avec le serveur, pas uniquement du XML, et notamment du JSON.
Note
AJAX permet donc aux pages HTML d’un site de communiquer avec l’API JSON de ce même site.
Exemple simple¶§
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);
});
Note
La fonction fetch
est disponible dans les navigateurs modernes.
Elle succède à l’ancienne méthode, nommée XMLHttpRequest
,
encore très utilisée.
La fonction fetch
¶§
- Elle prend en premier paramètre une URL.
- Elle accepte en deuxième paramètre (facultatif) un objet,
pouvant notamment contenir les attributs suivants :
method
indique la méthode HTTP à utiliser (GET par défaut),headers
contient les en-têtes à utiliser (sous forme d’un objet JS),body
contient, le cas échéant, le contenu de la requête ;- plus d’information : http://devdocs.io/dom/request/request.
- Elle retourne une Promesse.
Exemple de requête POST :
fetch("/api/Appointments/", {
method: 'POST',
headers: { "content-type": "application/json" },
body: JSON.stringify(appointment_data),
}).then((response) => {
if (!response.ok) { throw new Error("POST failed"); }
}).catch((err) => {
alert(err);
});