Plan du cours
Asynchronisme utilisé pour résoudre des problèmes de blocage.
Plan :
-
Chargement des pages Web
- AJAX : requêtes HTTP (a)synchrones
- Présentation, objectifs
- Échanges de données au format JSON
- Requêtes avec
XMLHttpRequest
- Asynchrone pour résoudre des blocages
- Boucle des évènements
Chargement des pages Web
Chargement des pages Web
- Récupération de la page HTML
- Lecture du document HTML au fur et à mesure
- Nœuds balise, texte :
rajoutée au DOM au fur et à mesure - Feuille CSS externe :
chargement de la feuille en parallèle (non bloquant) - Balises images, vidéos :
le fichier est chargé en parallèle (non bloquant) - JavaScript :
chargement du fichier JS puis exécution
Attention : Bloque la construction du DOM et du CSS !
- Nœuds balise, texte :
Problèmes de chargement
Erreur en cas d’interaction trop tôt.
Chargement des scripts
Solution pour que le DOM soit prêt lors de l’exécution du script :
- Mettre la balise
<script>
à la fin :- Inconvénient : Ne télécharge pas le script avant d’arriver à la fin du document.
Chargement des scripts
Solution pour que le DOM soit prêt lors de l’exécution du script :
- Attendre la fin du chargement du
DOM
(événementDOMContentLoaded
) avant de lancer le script :- Avantage : balise
<script>
où l’on veut. - Inconvénient : Le téléchargement du script ne se fait pas en parallèle du chargement du DOM.
- Avantage : balise
Rappel : L’événement DOMContentLoaded
est lancé quand le document a été
chargé et analysé, sans attendre les CSS, images, …
document.addEventListener("DOMContentLoaded",
function() {
// code qui nécessite le chargement complet du DOM
});
Chargement des scripts
Solution pour que le DOM soit prêt lors de l’exécution du script :
<script src="..." defer></script>
: (Recommandé)
Le script est chargé en parallèle de la lecture du DOM et ne sera exécuté qu’une fois le DOM prêt.- Avantage : Les scripts sont exécutés dans l’ordre.
- Inconvénient : Comme ils sont exécutés dans l’ordre, un
<script defer>
ne peut s’exécuter qu’après le chargement des<script defer>
précédents.
Note : Seulement pour les scripts à télécharger (= avec attribut src
).
Chargement des scripts
Pour information, autre manière de charger un script :
<script src="..." async></script>
:
Le script est téléchargé en parallèle de la lecture du DOM (non bloquant) et exécuté dès qu’il est disponible.
Attention :
- Le DOM n’est pas forcément prêt.
- Les scripts ne sont pas forcément exécutés dans l’ordre où ils sont déclarés.
Récapitulatif
On vous conseille généralement de privilégier defer
.
AJAX :
Requêtes HTTP (a)synchrones
Utilisation classique d’un serveur
Utilisation classique d’un serveur
Utilisation classique d’un serveur
Utilisation classique d’un serveur
Utilisation plus dynamique
Utilisation plus dynamique
Utilisation plus dynamique
Exemples d’utilisation de AJAX
-
Autocomplétion des recherches Google
-
Zoom sur les cartes OpenStreetMap
-
Défilement infini dans un fil Twitter
Avantages de AJAX
Diminution du temps de latence :
- pas de page Web à recharger
- la page reste utilisable pendant une requête AJAX
Ciblage :
- affecte seulement une partie de la page
Exemple : la<div>
qui accueille les noms de ville - moins d’échanges de données
Amélioration de l’expérience utilisateur :
- Possibilité de réagir en direct aux actions de l’utilisateur
Technologies utilisées
-
JavaScript
pour l’objet qui gérera la requête au serveur (objetXMLHttpRequest
) ; -
PHP
côté serveur pour communiquer avec la base de données (ou un autre langage côté serveur). -
JSON
comme format de données pour les échanges entre client et serveur.
JSON
= JavaScript Object Notation. -
L’ensemble de ces technologies est regroupé sous le nom
AJAX
AJAX
= Asynchronous JavaScript And XML
(car le format de données XML était plus utilisé avant)
AJAJ
= Asynchronous JavaScript And JSON
Technologies utilisées
Technologies utilisées
Technologies utilisées
Technologies utilisées
Format JSON
Exemple d’un fichier de données au format JSON
Traduction JavaScript → JSON
class Point {
constructor (x,y,couleur,marqueur) {
this.x = x;
this.y = y;
this.couleur = couleur;
this.marqueur = marqueur;
}
}
let A = new Point(5,-3,"rouge","croix");
console.log(A);
// → Point { x:5, y:-3, couleur:'rouge', marqueur:'croix' }
JSON.stringify(A);
// → '{"x":5,"y":-3,"couleur":"rouge","marqueur":"croix"}'
Traduction JSON → JavaScript
let text = '{"x":2,"y":5,"couleur":"jaune","marqueur":"rond"}'
let point = JSON.parse(text)
console.log(point);
// Affichage de la sortie DevTools
// → Object { x:2, y:5, couleur:'jaune', marqueur:'rond' }
Remarque : PHP sait aussi lire et écrire le JSON
json_encode($var_php);
json_decode($json_string);
Donc JSON permet à PHP et JS de communiquer !
L’interface XMLHttpRequest
- Sert à interagir avec un serveur HTTP :
- envoyer une requête HTTP
- lire la réponse HTTP
- Il est instancié ainsi :
let xhr = new XMLHttpRequest();
- Il dispose de méthodes pour ouvrir une requête, l’envoyer, l’abandonner, connaître l’évolution de son statut, connaître le contenu de la réponse.
Rappel : requête/réponse HTTP
Demander une page Web, c’est envoyer une requête HTTP à un serveur HTTP :
GET /~rletud/index.html HTTP/1.1
Host: webinfo.iutmontp.univ-montp2.fr
Le serveur renvoie alors sa réponse HTTP, qui contient la page Web demandée dans son corps :
HTTP/1.1 200 OK
Content-Length: 65585
Content-Type: text/html
Last-Modified: Wed, 09 Apr 2014 10:48:09 GMT
<!doctype html>
<html>...
Rappel : requête/réponse HTTP
Requête :
- la méthode de la requête (GET, POST, DELETE, PUT)
- le chemin de la ressource
- la version du protocole HTTP
- le Host, c.-à-d. le nom de domaine du serveur HTTP
Réponse :
- la version du protocole HTTP
- l’état (status) de la réponse sous forme numérique et texte :
(erreur si ≥ 400)- 200 OK
- 304 Not Modified
- 404 Not Found
- 500 Internal Server Error
Requêtes POST
Les requêtes HTTP de type POST possèdent un corps de requête en plus de l’en-tête.
Le corps de la requête HTTP sert ici à envoyer les informations.
POST /~rletud/traitePost.php HTTP/1.1
Host: localhost
Content-Length:14
Content-Type:application/x-www-form-urlencoded
prenom=Marc&nom=Assin
Méthodes de XMLHttpRequest
-
la méthode
open
(ouvre la requête)xhr.open("GET", "http://www.google.fr", true);
Le 3ème argument indique si la requête est asynchrone
-
la méthode
send
(envoie la requête avec un corps de requête)xhr.send(contenu)
- si la méthode est
GET
, contenu estnull
; - si la méthode est
POST
, contenu est- soit
null
- soit égal à une chaîne du type
param1=valeur1¶m2=valeur2&…
- soit
- si la méthode est
Attributs de XMLHttpRequest
-
l’attribut
readyState
: Il indique l’état de réception des données :Valeur État Description 0 xhr.UNSENT
Le client a été créé. open()
n’a pas encore été appelé.1 xhr.OPENED
open()
a été appelé.2 xhr.HEADERS_RECEIVED
send()
a été appelé, et les en-têtes et le statut sont disponibles.3 xhr.LOADING
Téléchargement ; responseText
contient des données partielles.4 xhr.DONE
L’opération est terminée. -
l’attribut
responseText
Il contient, sous forme d’une chaîne de caractères, les données en réponse à la requête. Il n’est complet que si
readyState
est à la valeur 4. -
l’attribut
status
Code d’état de la réponse (par ex.200 OK
)
Requête synchrone avec XHR
Il suffit de :
- créer une instance de la classe
XMLHttpRequest
- initialiser la requête et écriture son en-tête avec
open
- écrire le corps de la requête et l’envoyer avec
send
Après send
, la réponse HTTP (le status, le document …) est écrit dans ce
même objet.
let req = new XMLHttpRequest();
req.open('GET', 'https://romainlebreton.github.io/', false);
req.send(null); // null: corps de la requête vide si GET
console.log(req.status); // -> 200
console.log(req.responseText.substring(0,100));
// -> <!DOCTYPE html><html>...
Défaut d’une requête synchrone
Inconvénients d’une requête synchrone :
-
Le
send
est bloquant, c’est-à-dire que le JavaScript reste bloqué sursend
tant que l’on n’a pas reçu la réponse du serveur. -
C’est d’autant plus gênant que la connexion est mauvaise, le serveur est lent ou le fichier renvoyé est gros !
Remarque : C’est le false
de req.open('GET', url, false)
qui fait que la
requête est synchrone.
Défaut d’une requête synchrone
Exemple de blocage avec une requête synchrone
url = "cityRequest.php?name=Vi";
let httpRequest = new XMLHttpRequest();
// false désactive l'asynchronisme
httpRequest.open("GET", url, false);
httpRequest.send(null);
console.log(httpRequest.response);
Les événements ne se déclenchent pas sur le navigateur ! Pourquoi ??
Réponse : Le code JavaScript s’exécute sans parallélisme dans le thread principal jusqu’à son terme. Il ne peut pas traiter d’évènements (clavier, animation, …) tant qu’il est occupé.
Requête a
synchrone avec XHR
Programmation asynchrone :
- style de programmation dans lequel les tâches sont exécutées en parallèle
- permet à plusieurs tâches de s’exécuter en même temps, sans attendre la fin de chaque tâche avant de passer à la suivante.
Pour XMLHttpRequest
:
On active l’a
synchronisme avec req.open('GET', url, true)
Requête a
synchrone avec XHR
Piège :
let req = new XMLHttpRequest();
req.open ("GET", "https://romainlebreton.github.io", true);
req.send(null);
console.log("Réponse :" + req.responseText);
// Réponse vide !
Solution :
Il faut un mécanisme pour notifier au client que la requête est terminée :
Écoute de l’événement "load"
Exemple :
let req = new XMLHttpRequest();
req.open ("GET", "https://romainlebreton.github.io", true);
req.addEventListener("load",
function() {
console.log ("Réponse :" + req.responseText.substring(0,100));
}
);
req.send(null);
Requête a
synchrone POST
Pour ressembler à l’envoi d’un formulaire POST
let xhr = new XMLHttpRequest()
url = "https://webinfo.iutmontp.univ-montp2.fr/~rletud/traitementPost.php"
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.addEventListener("load",
function() {
console.log ("Réponse :" + xhr.responseText.substring(0,100));
}
);
xhr.send("nom_var=AssinMarc")
Autre façon de faire : utiliser les FormData
La boucle des évènements
Boucle des évènements
La programmation asynchrone amène des tâches à s’exécuter en parallèle :
- Sur le thread principal : DOM, affichage, JavaScript
- Sur des thread parallèles : réseau, clavier, souris, cryptographie
JavaScript gère la concurrence entre tâches parallèles grâce à une « boucle d’événements » :
- Les
callback
des évènements asynchrones sont empilés dans la pile de tâches - Boucle des évènements (
Event loop
):- Exécution du JavaScript dans le thread principal jusqu’à son terme
- Quand le thread principal n’a plus de code à exécuter, il dépile une tâche et l’exécute jusqu’à son terme
- Et il boucle ainsi de suite
Boucle des évènements
Exemple : Qu’affiche le programme suivant ?
console.log("Étape 1.");
setTimeout(function etape2 () { console.log("Étape 2.");}, 0);
console.log("Étape 3.");
Réponse :
// Étape 1.
// Étape 3.
// Étape 2.
Pourquoi ?
Parce que le callback de setTimeout
est rajouté sur la pile des
tâches, et ne sera exécuté qu’à la fin du JavaScript “principal”.