Langages Dynamiques

Cours 4
JSON
AJAX
Asynchronisme
Promesses

kn@lri.fr

Tableaux

Array

Les tableaux (classe Array) font partie de la bibliothèque standard Javascript. On peut créer un tableau vide avec [].

let tab = []; tab[35]; //undefined tab[35] = "Hello"; //initialise la case 35 à "Hello" tab[0]; //toujours undefined; tab.length; //36 ! indice le plus grand ayant été //initialisé + 1

Array (interface impérative)

new Array(n)
Initialise un tableau de taille n (indicé de 0 à n-1) où toutes les cases valent undefined
.length
renvoie la longueur du tableau
.toString()
applique .toString() à chaque élément et renvoie la concaténation
.push(e)
ajoute un élément en fin de tableau
.pop()
retire et renvoie le dernier élément du tableau (undefined si vide)
.shift()
retire et renvoie le premier élément du tableau (undefined si vide)
.unshift(e)
ajoute un élément au défbut du tableau
.splice(i, n, e1, …, ek)
à partir de l'indice i, efface les éléments i à i+n-1 et insère les éléments e1, …, ek
.sort(f)
trie le tableau en place suivant la fonction f, qui compare deux éléments et doit renvoyer un nombre négatif, nul ou positif selon que le premier argument est inférieur, égal ou supérieur au second. Si f est omise, alors les éléments sont comparés selon leur valeur de chaînes de caractères.

Array (interface fonctionnelle)

.forEach(f)
Applique la fonction f à tous les éléments du tableau. f reçoit trois arguments (v, i, t) :
v
la valeur courante de la case visitée
i
l'indice courant (à partir de 0)
t
le tableau en entier
.map(f)
Applique la fonction f à tous les éléments du tableau et renvoie le tableau de résultat. f reçoit trois arguments (v, i, t), comme pour .forEach.
.filter(f)
Renvoie une copie du tableau dans laquelle se trouve tous les éléments du tableau initial pour lesquels f renvoie true.
.any(f)
Renvoie true si et seulement si f renvoie true pour un des éléments.
.every(f)
Renvoie true si et seulement si f renvoie true pour tous les éléments.

Affectation par deconstruction (tableaux)

let couleurs = [ "rouge", "bleu", "vert", "jaune", "violet" ]; //avant; let c_rouge = couleurs[0]; let c_bleu = couleurs[1]; let c_vert = couleurs[2]; // maintenant let [ c_rouge, c_bleau, c_vert ] = couleurs; // autres cases ignorées let [ c_rouge, c_bleau, c_vert, ...autres ] = couleurs; // autres est un tableau à 2 cases [ "jaune", "violet" ]

Fonctions variadiques

En Javascript, on peut toujours appeller une fonction avec un nombre quelconque d'arguments.

Si la fonction est définie avec plus de paramètres, les paramètres manquant sont initialisé à undefined

Si la fonction est définie avec moins de paramètres, les arguments supplémentaires sont ignorés

On peut définir des fonctions variadiques:

function f (a, b, ...others) {// attentions, le ... // fait partie de la syntaxe console.log(others); }

others est un tableau contenant les arguments en plus. Il ne peut y avoir qu'un seul argument ...x qui doit être le dernier paramètre.

Boucles « for each »

On peut itérer sur une collection avec la construction for … of. Plusieurs objets de la bibliothèque standard implémentent la méthode .entries() qui renvoie la collection des entrées :

let tab = [ "A", "B", "C", "D" ]; for (let e of tab) { console.log(e); // affiche "A" "B" "C" "D" } for (let e of tab.entries()) { console.log(e); // affiche [0,"A"] [1,"B"] [2,"C"], [3,"D"] } for (let [i,e] of tab.entries()) { //avec un let destructurant console.log(i, e); // affiche 0 "A" 1 "B" 2 "C" 3 "D" }

JSON

JSON (ou, le nouveau XML)

Il est souvent utile de pouvoir échanger de l'information « structurée » entre applications

Solutions actuelles

JSON : syntaxe

Une valeur JSON est représenté par un sous-ensemble de la syntaxe Javascript pour les objets.

{ "nom" : "Nguyen", "prénom" : "Kim", "cours": [ "Javascript", "TER" ], "full time" : true, "age" : 3.6e1, "hobby" : null }

JSON : syntaxe (suite)

API pour JSON

L'objet JSON disponible en Javascript possède deux méthodes :

JSON.parse (exemples)

JSON.parse("1"); > 1 JSON.parse("[ 1, 2, \"3\", false ]"); > [1, 2, "3", false] JSON.parse("null"); "\a" > null var o = JSON.parse("{ \"a\" : 1 }"); > undefined o.a; > 1 JSON.parse("{ a : 1 }"); > erreur JSON.parse("{ "); > erreur JSON.parse("undefined"); > erreur

JSON.stringify

Beaucoup de règles :

JSON.stringify (exemple)

class Point { constructor(x, y) { this.x = x; this.y = y; this.f = function () {}; } move (i, j) { this.x += i; this.y += j; } } let p = new Point(1,2); JSON.stringify(p); > "{\"x\":1,\"y\":2}"

Les propriétés du prototype n'ont pas été énumérées, celle de l'objet qui est une fonction (f) a été ignorée

Avantages/Inconvénients de JSON par rapport à XML

Une fois parsée, la valeur JSON devient une valeur Javascript (utilisation directe des propriétées)

Une valeur XML devient un objet DOM, dans lequel il faut naviguer avec .getFirstChild(), .getElementById(), ...

Par contre JSON ne possède pas de notion de schéma bien établie (draft en cours) donc il faut valider « à la main »

AJAX

Asynchronous Javascript and XML

Une application Web doit parfois échanger avec un serveur Web :

Utilisation de formulaires HTML :

AJAX : API permettant d'envoyer des requêtes HTTP à un serveur depuis Javascript de manière asynchrone, en tâche de fond et de récupérer le résultat

XmlHttpRequest

Objet contenant les (nombreuses) méthodes permettant d'envoyer une requête GET ou POST à un serveur distant :

new XmlHttpRequest()
création de l'objet
.open(method, url, async)
crée une requête HTTP avec la méthode method (valant "GET" ou "POST"), vers l'url url. Le booléen async (vrai par défaut) exécute un envoi asynchrone. On le laissera toujours à vrai.
.send()
envoie la requête au serveur. L'appel retourne immédiatement si async valait vrai lors de l'ouverture
.responseText
contient la réponse du serveur sous forme de chaîne de caractères
.status
le code HTTP de la réponse du serveur
.readyState
Un entier entre 0 (requête non envoyée) et 4 (résultat disponible)

Traîtement asynchrone de la réponse

On se connecte à l'évennement readystatechange de l'objet XmlHttpRequest. Exemple:

Avantage/Inconvénients

Avantages :

Inconvénient

On peut demander un résultat d'un autre type que string (document HTML, objet JSON, …)

Démo

On souhaite faire une boite de texte qui propose de la complétion à partir d'un dictionaire en français.

On doit écrire une partie serveur et une partie client.

Traîtements Asynchrones

Rappels

Ces notions sont distinctes et toutes présentes en Javascript

Modèle d'exécution Javascript

Le moteur d'exécution Javascript est mono-thread. Il ne permet donc pas de calculs parallèles. Si on effectue un calcul coûteux (en temps), la page se « fige ». Les navigateurs interdisent ce genre de comportements.
Deux implications sur le code Javascript :

  1. Les calculs « lents » à cause de condition externes (requêtes réseaux, une attente d'évènement utilisateur, …) sont effectués de manière asynchrone
  2. Les calculs intrinsèquement « coûteux » doivent être « découpés » manuellement par le programmeur.

On va explorer au travers d'exemple comment écrire du code respectant ces contraintes en « pur javascript simple™ »

(Le cours 6 montrera les nouveautés du standard ECMAScript 6 et 7 qui permettent de faire ça de manière élégante)

Mots mis en formes

On veut modifier l'exemple précédant du dictionnaire pour appeler, pour chaque mot renvoyé, un deuxième service Web qui met ce mot en gras

Promesses

Callback pyramid of Doom

//step 1 appelle une continuation en lui passant //la valeur qu'elle calcule de manière asynchrone step1 (function (value1) { … step2(function (value2) { … step3(function (value3) { … step4(function (value4) { // on peut enfin faire quelque chose avec value4 } } } }

Promesses

Problèmes du code asynchrone

Solution : promesses (promises). Permettent de simuler du code séquentiel

Utilisation :

let p = new Promise(function (success, failure) { // faire quelque chose d'asynchrone // quand le resultat r est disponible faire success(r); // si une erreur e se produit faire : failure(e); })

L'objet Promise contient deux méthodes :

Comment ré-écrire le code ?

Promesses

let step1 = function (value1) { return new Promise(function(success, failure) { //faire ce qu'on faisait avant. }; }; ... var step4 = function (value4) { return new Promise(function(success, failure) { //faire ce qu'on faisait avant. }; };

Promesses

let p = new Promise(…); p.then(step1) .then(step2) .then(step3) .then(step4) .catch(function(e) { console.log(e); };

Autre méthodes utiles :

Support syntaxique, fonctions async

L'utilisation des promesses est encore simplifiée par une syntaxe spéciale
async function longFun() { // code qui prend du temps return 4 } Les fonction async ne renvoient pas directement leur résultat, mais une promesse qui le calcule.
let x = longFun(); //x ne vaut pas 42! x.then((res) => { console.log(res); }); Au sein d'une fonction async on peut utiliser le mot clé await pour « attendre » une promesse : async otherLongFun () { let x = await longFun(); let y = await longFun(); let z = await longFun(); return x + y + z; }