0% ont trouvé ce document utile (0 vote)
110 vues51 pages

Module Web Avance 1

Le document décrit NodeJS et ses concepts clés comme l'event loop et l'architecture mono-thread. Il présente également les bases du langage Javascript comme la déclaration de variables, de fonctions, de boucles et le contexte d'exécution.

Transféré par

iaqowefsccydbebytv
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
110 vues51 pages

Module Web Avance 1

Le document décrit NodeJS et ses concepts clés comme l'event loop et l'architecture mono-thread. Il présente également les bases du langage Javascript comme la déclaration de variables, de fonctions, de boucles et le contexte d'exécution.

Transféré par

iaqowefsccydbebytv
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
Vous êtes sur la page 1/ 51

Module: Web Avancé

Partie 1: A la découverte de Javascript et NodeJS


2023 - Corentin Mors

X
Javascript et NodeJS

2
Vous avez dit NodeJS ?

● Créé par Ryan Dahl en 2009


● Licence MIT
● Dernière release LTS : v18
● Basé sur V8 Engine de Google
● +99 000 packages
● Permet de faire du javascript, côté serveur
● Non Blocking I/O
● Single Thread with Event Loop
● Windows, Linux, Mac
● Un seul Langage pour le Frontend et le Backend
3
L'architecture de NodeJS
while (queue.attendreMessage()) {
queue.traiterProchainMessage();
}
● Stack (pile d'appels) : Les appels de fonction forment
une pile de cadre (frames).
● Heap (le tas) : Les objets sont alloués en mémoire dans
un tas qui désigne une zone de la mémoire sans
structure particulière.
● Queue (la file) : Un environnement d'exécution
JavaScript (runtime) contient une queue de messages à
traiter. Chaque message est associé à une fonction.
Lorsque la pile est vide ou a suffisamment d'espace, on
retire un message de la queue et on le traite. Le
traitement consiste à appeler la fonction associée au
message (et donc à créer le cadre dans la pile d'appel).
Le traitement d'un message est fini lorsque la pile
d'appels redevient vide.

4
L'event loop, un mono-thread A l'inverse un serveur Apache a
un thread par client.

Quand une requête est reçue elle est ajoutée à la queue qui est consommée par l'event loop. Chaque
opération est effectuée dans le mono-thread, une par une. Si une opération I/O bloquante a besoin
d'accéder à la base de donnée par exemple, on utilise un pool de thread C++. On assigne l'opération à un
thread du pool qui répondra une fois celle-ci terminée via un callback à l'event loop puis au client.

La libraire libuv gère le threading sous-jacent


5
Exemple d'opération synchrone bloquante

const crypto = require("crypto");


for (let i=0; i < 2; i++) {
const hash = crypto.pbkdf2Sync("secret", "salt", 10000, 512, "sha512");
console.log(hash);
}

Password-Based Key
Derivation Function : utilisée
par exemple par certains
gestionnaires de mots de passe

6
Exemple d'opération asynchrone non-bloquante

const crypto = require("crypto");


for (let i=0; i < 2; i++) {
crypto.pbkdf2("secret", "salt", 10000, 512, "sha512",
(error, hash) => {
console.log(hash);
}
);
}

Il n'y a aucune garantie sur l'ordre de


retour des callback ici, tout dépend de quel
thread répondra le + vite à l'event loop.

7
4 threads dans le pool par défaut

Certaines opérations effectuées


nativement par l'OS comme les
appels réseaux n'utilisent pas ce
pool de thread. On n'a donc pas le
même comportement quand on
fait des requêtes http dans une
boucle par exemple.

• NodeJS utilise un pool de thread pré-alloué.


• Le nombre de thread dans ce pool est 4 (par défaut).
8
Résumé: tout s'exécute en parallèle, sauf votre code

9
Du côté des navigateurs
http://latentflip.com/loupe/

10
Les bases du Javascript

11
Déclarer des variables

var varVariable = "something";

let letVariable = "i'll change it later";

const constVariable = "it will stay like this forever";

Il existe trois manières de déclarer des variables en JavaScript.

● var
● let
● const

12
Limite de bloc d'instruction

let greeting = "say Hi";

if (true) {

let greeting = "say Hello instead";

console.log(greeting); // "say Hello instead"

console.log(greeting); // "say Hi"

Les variables déclarées avec let et const sont uniquement disponible dans le contexte du bloc
dans lequel elles sont définies.

Elles ne peuvent pas être déclarées deux fois dans le même contexte.
13
Pourquoi il ne faut pas utiliser var ?

var greeter = "hey hi";

var times = 4;

if (times > 3) {

var greeter = "say Hello instead";

console.log(greeter) // "say Hello instead"

● var a une portée globale


● var peut être redéclaré
● var est initialisé à undefined par défaut

14
Comportement de const

const greeting = "say Hi";

greeting = "say Hello instead"; // error: Assignment to constant variable.


const greeting = "say Hello instead"; // error: Identifier 'greeting' has already been
declared

const greeting = {
message: "say Hi",
times: 4
}

greeting.message = "say Hello instead";

const ne peut pas être redéclaré, ni affecté à nouveau.


Cependant, on peut modifier ses propriétés, la variable n'est pas immuable, par exemple on peut
modifier les propriétés d'un objet ou ajouter des éléments dans un tableau.
15
Déclarer des fonctions

const greetings = () => {

console.log("Hello World")

function greetings() {

console.log("Hello World")

Il existe 2 façons de déclarer des fonctions:

● const name = (parameter) => { … };


● function name(parameter) { … }

16
Déclarer des objets ou des tableaux

const myArray = ["tata", "titi"];

myArray.push("toto");

console.log(myArray.pop()); // "toto" → ["tata", "titi"]

console.log(myArray.shift()); // "tata" → ["titi"]

console.log(myArray[0]); // "titi"

const myObject = {};

myObject.firstname = "John";

myObject["name"] = "Doe";

17
Les boucles et itérations

for (let pas = 0; pas < 5; pas++) {


console.log("Faire " + pas + " pas vers l'est");
}

for (let value of ["a", "b", "c"]) {


console.log(value);
}

const object = { a: 1, b: 2, c: 3 };

for (const property in object) {


console.log(`${property}: ${object[property]}`);
}

Les instructions do…while, break, continue


existent aussi en Javascript
18
Le contexte d'exécution

• L'environnement (ou la portée) dans lequel une ligne JS est exécutée


est appelé contexte d'exécution.

• Le runtime JavaScript maintient une pile de ces contextes d'exécution,


et le contexte d'exécution présent au sommet de cette pile est celui en
cours d'exécution.

• L'objet auquel « this » fait référence change chaque fois que le


contexte d'exécution est modifié.

• Le moteur JS (e.g. V8) créera un contexte d’exécution globale lors de


l’exécution d’un code JS

• Le moteur JS a à sa disposition une mémoire globale (global scope):


– Variables globales
– Déclarations de fonctions
19
Le contexte d'exécution : phase de création

On détermine plusieurs éléments dans cette phase:

● L’objet global dans le cas du contexte d’exécution global (window dans un navigateur web)
● La valeur de this
● L’environnement lexical extérieur : C’est une référence vers l’environnement lexical englobant
l’environnement lexical du contexte d’exécution.

Le moteur effectue du “hoisting” : tout le code du contexte d’exécution est parcouru et l’espace mémoire
est réservé pour toutes les fonctions et variables de son environnement lexical. Les fonctions sont
stockées entièrement tandis que les variables sont initialisé à “undefined”.

Le hoisting permet d’appeler une fonction avant sa déclaration. Pour ce qui est des variables, si on appelle
une variable avant sa déclaration, elle aura bien une valeur: undefined. Cependant, si elle n’a pas été
déclarée, JavaScript renverra une erreur.

20
Phase de création en exemple (1)

var num = 2;

function pow(num) {
return num * num;
}

21
Phase de création en exemple (2)

var num = 2;

function pow(num) {
return num * num;
}

res = pow(num);

22
Le contexte d'exécution : phase d'exécution

Le code est exécuté ligne par ligne de façon synchrone. function f1() {
var age = 18;
Lorsqu'on appelle une variable, le context d'exécution va d'abord f2();
regarder si cette variable se trouve dans l'environnement lexical }
interne. Dans le cas contraire, il va regarder si elle est dans function f2() {
l'environnement lexical externe jusqu'à remonter à function a() {
l'environnement global. console.log(age);
}
a();
}

var age = 16;


f1();

23
Un peu de documentation

● Les différences entre var, let et const :


https://www.freecodecamp.org/french/news/quelle-est-la-difference-entre-var-let-et-const/
● Gestion de la concurrence et boucle des événements :
https://developer.mozilla.org/fr/docs/Web/JavaScript/Event_loop
● Don't block the event loop : https://nodejs.org/fr/docs/guides/dont-block-the-event-loop
● Les contextes d'execution en Javascript:
http://morganridel.fr/les-contextes-d-execution-en-javascript

Exercice: Installer NodeJS et créez votre premier script Hello World

24
Manipulations de tableaux et
d'objets

25
Array: filter
const new_array = arr.filter(function callback(element, index, array) {
// Return true or false
}[, thisArg])

const numbers = [1, 2, 3, 4];


const evens = numbers.filter(item => item % 2 === 0);
console.log(evens); // [2, 4]

const students = [
{ name: 'Quincy', grade: 96 },
{ name: 'Jason', grade: 84 },
{ name: 'Alexis', grade: 100 },
{ name: 'Sam', grade: 65 },
{ name: 'Katie', grade: 90 }
];

const studentGrades = students.filter(student => student.grade >= 90);

// [ { name: 'Quincy', grade: 96 }, { name: 'Alexis', grade: 100 }, { name: 'Katie', grade: 90 } ]

26
Array: map map crée une copie du tableau en
mémoire !

const new_array = arr.map(function callback(element, index, array) {


// Return value for new_array
}[, thisArg])

const numbers = [1, 2, 3, 4]; let numbers = [1, 2, 3, 4];


const evens = numbers.map(item => item*2); let filteredNumbers = numbers.map((num, index) => {
console.log(evens); // [2, 4,6,8] if (index < 3) { return num }
});

const persons = [
{firstname : "Malcom", lastname: "Reynolds"},
{firstname : "Kaylee", lastname: "Frye"},
{firstname : "Jayne", lastname: "Cobb"}
];

const p=persons.map(getFullName);//Malcom Reynolds,Kaylee Frye,Jayne Cobb

function getFullName(item) {
return [item.firstname,item.lastname].join(" ");
}

27
Array: reduce
const r=array.reduce(function(total, currentValue, currentIndex, arr) {
} , initialValue)

const numbers = [15.5, 2.3, 1.1, 4.7];


const val= numbers.reduce(getSum, 0);
function getSum(total, num) {
return total + Math.round(num);
}

const pets = ['dog', 'chicken', 'cat', 'dog', 'chicken', 'chicken'];

const petCounts = pets.reduce(function(obj, pet){


if (!obj[pet]) {
obj[pet] = 1;
} else {
obj[pet]++;
}
return obj;
}, {});

console.log(petCounts);
/* Output: { dog: 2, chicken: 3, cat: 1} */

28
Spread operator …

La syntaxe de décomposition permet d'étendre un itérable (par exemple une expression de


tableau ou une chaîne de caractères) en lieu et place de plusieurs arguments (pour les appels de
fonctions) ou de plusieurs éléments (pour les littéraux de tableaux) ou de paires clés-valeurs (pour
les littéraux d'objets).

const first = [1,2,3];


const second = [10,20,30];

const third = […first, …second];


const fourth = […first, 'a', …second, 'b'];

const first = {ville: 'Paris'};


const second = {job:'instructor'};

const third = {…first, …second};

29
Structure de données avancées

30
Set()

- Structure de donnée qui contient un ensemble de valeurs de n'importe quel type


- Possibilité d'itérer sur les valeurs dans leur ordre d'insertion
- Une valeur ne peut apparaître qu'une seule fois par Set (unicité)
- Performance de has() supérieure à includes() des Array

const monSet = new Set();

monSet.add(1); // { 1 }
monSet.add(5); // { 1, 5 }
monSet.add(5); // { 1, 5 } const o = { a: 1, b: 2 };
monSet.add("du texte"); // { 1, 5, 'du texte' } monSet.add(o);

monSet.has(1); // true monSet.add({ a: 1, b: 2 });


monSet.has(3); // false, 3 n'a pas été ajouté à l'ensemble // o fait référence à un objet différent
// il n'y a pas de problème pour cet ajout

monSet.size; // 3
monSet.delete(5); // retire 5 du set
31
Map()

- Contient des paires de clé-valeur (n'importe quel type de clé ou de valeur)


- L'ordre d'insertion est mémorisé
- Meilleures performances pour les additions/suppressions de clés valeurs qu'avec un objet

const contacts = new Map();


contacts.set("Jessie", {
phone: "01 99 00 12 34",
address: "1 Rue de l'avenue",
});
contacts.has("Jessie"); // true
contacts.get("James"); // undefined
contacts.set("James", { phone: "06 39 98 78 89", address: "3 Chemin du Parc" });
contacts.get("Jessie"); // {phone: "01 99 00 12 34", address: "1 Rue de l'avenue"}
contacts.delete("Miaouss"); // false
contacts.delete("Jessie"); // true
console.log(contacts.size); // 1

32
Les classes en Javascript

33
Déclarer une classe Pas de hoisting pour les classes !

Les classes se basent sur le modèle d'héritage et de class Rectangle {


prototypage standard de Javascript, c'est uniquement une constructor(hauteur, largeur) {
this.hauteur = hauteur;
syntaxe plus simple pour créer des objets et manipuler this.largeur = largeur;
l'héritage. }

get area() {
return this.calcArea();
}
On peut déclarer des champs publics :
calcArea() {
- En les déclarant à la racine de la classe return this.largeur * this.hauteur;
- En y accédant avec this.nomDeLaVariable }
}
On peut déclarer des champs privés en mettant un #
const carré = new Rectangle(10, 10);
devant.
console.log(carré.area);

34
Méthodes statiques
class Point {
constructor(x, y) {
Les méthodes static sont appelées par rapport à la
this.x = x;
classe entière et non par rapport à une instance this.y = y;
donnée. }

Principalement utilisées pour des fonctions static distance(a, b) {


utilitaires dans une application. const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}

const p1 = new Point(5, 5);


const p2 = new Point(10, 10);

console.log(Point.distance(p1, p2));

35
Les sous-classes

Le mot-clé extends utilisé dans les déclarations de class Chat {


classes permet de créer une classe qui hérite d'une constructor(nom) {
autre. this.nom = nom;
}
Quand on déclarer un constructeur dans une classe
parler() {
fille on doit utiliser super() avant this.
console.log(`${this.nom} fait du bruit.`);
}
super est utilisé pour appeler les fonctions
}
rattachées à un objet parent.
class Lion extends Chat {
parler() {
super.parler();
console.log(`${this.nom} rugit.`);
}
}

36
Les modules et packages

37
Chaque fichier est un module

En CommonJS:

● const test = require('./example.js') require inclut le fichier entier


● const http = require('http') tandis que import peut
sélectionner un composant précis
En ESM (ECMA Script Module):

● import * as name from 'module_name'


● import { name1, name2 } from 'module_name'

38
Les packages
"name": "my-api",
Un paquet est une arborescence de dossiers décrite par un "version": "1.0.0",
"description": "My description",
fichier package.json. Le paquet est constitué du dossier "source": "src/index.js",
contenant le fichier package.json et de tous les "main": "dist/main.js",
"types": "dist/types.d.ts",
sous-dossiers jusqu'au prochain dossier contenant un autre "type": "module",
fichier package.json, ou un dossier nommé "scripts": {
"start": "node dist/main.js",
node_modules. "build": "parcel build src/index.ts",
"watch": "parcel watch src/index.ts"
},
"author": "Me",
"license": "ISC",
Le fichier package.json contient les métadonnées de base "devDependencies": {
de votre application NodeJS comme par exemple son nom, "@types/express": "^4.17.17",
"@types/lodash": "^4.14.197",
sa description, les dépendances externes, les points },
d'entrée… "dependencies": {
"express": "^4.18.2",
"lodash": "^4.17.21",
}

39
Les gestionnaires de packages

Les gestionnaires de packages tel que npm vous permettent de télécharger des dépendances externes.

Elles seront ajoutées à votre fichier package.json et seront téléchargées dans node_modules.

Quelques commandes rapides:

- Initialiser un nouveau projet: npm init


- Ajouter une dépendance externe: npm install <name>
- Ajouter une dépendance externe de développement npm install --save-dev <name>
- Installer les dépendances exactes via le fichier package-lock.json: npm ci

Il existe d'autres gestionnaires de paquets avec des avantages différents tels que Yarn ou PNPM.

40
Asynchrone et NodeJS

41
Callback

fs.readFile(music, (error, data) => {


if (err) {
console.error(error);
}
console.log('data', data);
});

La fonction de callback est une fonction qui sera appelée une fois l'opération asynchrone
terminée.
Par convention:
1. le premier paramètre est une instance de la classe Error – si l’opération a échoué – ou
null;
2. le deuxième paramètre est le résultat de l’exécution de l’opération, dans le cas où elle
s’est exécutée sans erreur. C’est la valeur qu’on aurait passé à return si notre fonction
était synchrone.
42
Rappel: les opérations async s'exécutent en //

// setTimeout() est une fonction asynchrone qui exécute la fonction de callback


// après quelques millisecondes d'attente
setTimeout(() => console.log('a'), 50); // afficher a dans 50 millisecondes
setTimeout(() => console.log('b'), 90); // afficher b dans 90 millisecondes
setTimeout(() => console.log('c'), 20); // afficher c dans 20 millisecondes
// => ordre d'affichage: c, a, puis b

Quand on appelle plusieurs fonctions asynchrones de suite, leurs callback respectifs ne sont pas
forcément exécutés dans le même ordre !

43
Promises

fs.promises.readFile(music).then((data) => {
console.log('data', data);
}).catch((error) => {
console.error(error);
});

Le concept de promesse (Promise) a été intégré à Javascript pour simplifier le séquençage des
appels asynchrones et améliorer la visibilité.
Le résultat final de cette fonction peut être récupéré en appelant .then() et .catch().
Une promesse peut être:
● Résolue : resolve si l'opération s'est exécutée avec succès
● Rejetée : reject si une erreur s'est produite
new Promise(function (resolve, reject) {...}

44
Faire des chaînes de Promises

const incrementer = (valeur) => {


return Promise.resolve(valeur + 1);
}

incrementer(0) // (cette fonction retourne une Promise)


.then((résultat) => {
// résultat === 1
return incrementer(résultat); // (on retourne à nouveau une Promise)
})
.then((résultat) => {
// résultat === 2
return incrementer(résultat); // (on retourne à nouveau une Promise)
})
.then((résultat) => {
// résultat === 3
console.log('résultat final:', résultat);
});

45
Async/Await

async function logFile () {


try {
const data = await fs.promises.readFile(music);
console.log('data', data);
} catch (error) {
console.error(error);
}
}

async et await ont été ajoutés à Javascript pour simplifier et rendre plus lisible la définition et
l'appel des fonctions asynchrones à base de promesses.

await attend la fin d'une opération avant d'effectuer la suivante, l'ordre est garanti.

Pour intercepter les retours d'erreurs on utilise try {} catch (err) {}


46
Promisify

C'est une méthode de la librairie standard NodeJS qui convertit une méthode dont la réponse est sous
forme de callback en une réponse sous forme de promesse.

const util = require('util')

// Importing File System module


const fs = require('fs')

const readdir = util.promisify(fs.readdir)

readdir('process.cwd()')
.then(files => {
console.log(files)
})
.catch(err => {
console.log(err)
})

47
Un peu de documentation

● Fonctions synchrones vs asynchrones : https://adrienjoly.com/cours-nodejs/sync-vs-async.html


● La promisification : https://fr.javascript.info/promisify

Exercice: recréez la fonction cat en NodeJS en utilisant de l'asynchrone

48
Autres

49
Les émetteurs d'évènements

NodeJS inclut une librairie standard nommée events qui permet de gérer des événements.

let eventEmitter = require('events').EventEmitter;

eventEmitter.emit("monEvenement", {"donneeA":"1","donneeB":"2"});

eventEmitter.on("monEvenement", function(data) {
console.log(data.donneeA);
console.log(data.donneeB);
});

50
Polyfilling

• Polyfilling est un terme utilisé pour remplacer une méthode existante dans la spécification
JavaScript, mais qui n’est pas encore prise en charge par un moteur JavaScript particulier.

51

Vous aimerez peut-être aussi