SlideShare une entreprise Scribd logo
1  sur  59
Télécharger pour lire hors ligne
Javascript
Les générateurs
Préambule ...
Nous allons parler ici de fonctionnalités qui ne
sont disponibles que depuis ECMAScript 6.
Leurs support n’est pas généralisé ; par exemple
pas de IE, ou bien pas de Node.js (sauf si
activation de --harmony )
http://kangax.github.io/compat-table/es6/#generators
https://github.com/google/traceur-compiler
Itérateurs ?
En programmation, nous utilisons des boucles.
Itérateurs ?
for (var i = 0; i < 10; i++) {
// Faire quelque chose 10 fois.
}
while(condition) {
// Faire quelque chose jusqu'à ce que ...
}
Le principe est de répéter l'exécution d’un code
un certain nombre de fois.
Chaque “tour” de notre boucle est une itération.
Parcourir cette boucle se dit donc itérer.
Itérateurs ?
Il est aussi possible d’itérer sur un tableau :
Itérateurs ?
var valeurs = [ 'a', 'b', 'c' ];
// Itérer sur les indices du tableau :
for (var indice in valeurs) {
console.log(valeurs[indice ]);
}
// Ou utiliser la méthode Array.forEach()
valeurs.forEach(function(valeur) {
console.log(valeur);
});
Ou un objet :
Itérateurs ?
var valeurs = {a : 1, b : 2, c : 3 };
// Itérer sur les propriétés :
for (var indice in valeurs) {
if (valeurs.hasOwnProperty(indice)) {
console.log(
'[%s] => %s',
indice,
valeurs[indice]
);
}
}
On considère alors que les tableaux et les objets
sont des itérateurs.
Mais comme on vient de le voir, on itère sur les
indices ou propriétés.
C’est pas très pratique.
Itérateurs ?
ECMAScript 6 (es6) introduit une nouvelle
syntaxe d’itération : for ( … of …) :
Itérateurs ?
var valeurs = [3, 4, 5] ;
for (var indice in valeurs) {
console.log(indice);
}
// 0 ... 1 ... 2
for (var valeur of valeurs) {
console.log(valeur);
}
// 3 ... 4 ... 5
o/ joie, bonheur, alacrité nous pouvons donc
désormais itérer sur des tableaux et objets sans
recoder la roue ou utiliser des Frameworks
(jQuery $.each, par exemple).
Itérateurs ?
Seulement, dehors il y a le vrai monde...
Itérateurs ?
Et le vrai monde, il veut itérer, et si possible de
manière optimisée et pas que sur des tableaux et
des objets.
Petit interlude philosophie, optionnel.
Cliquez pour visionner.
Pourquoi c’est nul ?
L’algorithme consistant à remplir un
tableau/objet puis d’itérer dessus a beau avoir
fait la route avec nous depuis nos premières
pages, il présente de sérieux défauts.
Dénigrons !
1.
Il veut même pas rendre ses ressources. Radin.
Dénigrons !
Le tableau/objet est généré puis stocké en
mémoire entièrement pendant toute l’itération
de notre boucle, même si nous n’avons plus
besoin de ces valeurs.
Dénigrons !
// Création de ma liste.
var liste = [];
// Remplissage
for (var i = 0; i < 1000000; i++) {
liste.push(Math.random());
}
// Affichage
for (var val of liste) {
console.log(val);
}
Si l’on considère le code suivant :
L’importante partie de mémoire utilisée pour
stocker les 1 million de valeurs va rester allouée
pendant toute l’itération de la boucle ; même si
nous n’avons plus besoin de ces valeurs après le
console.log().
2.
Il a la manie de tout bloquer.
Dénigrons !
Pendant toute la création puis l’itération du
tableau/objet, le navigateur ou le processus est
“occupé”. Cela peut retarder la gestion d’
évènements, ralentir des affichages et rendre
instable le navigateur/programme.
Dénigrons !
// Création de ma liste.
var liste = [];
// Remplissage
console.time('Remplissage du tableau');
for (var i = 0; i < 1000000; i++) {
liste.push(Math.random());
}
console.timeEnd('Remplissage du tableau');
// Affichage
console.time('Affichage du tableau');
for (var val in liste) {
console.log(val);
}
console.timeEnd('Affichage du tableau');
// Remplissage du tableau: 2054.109ms
// Affichage du tableau: 2185.662ms
Ajoutons un timer à notre code :
Ici, on voit que l’on a été “bloqué” pendant deux
fois 2 secondes.
Faire mumuse sur JSFiddle
Dénigrons !
// Remplissage
console.time('Remplissage du tableau');
for (var i = 0; i < 1000000; i++) {
liste.push(Math.random());
}
console.timeEnd('Remplissage du tableau');
// Affichage
console.time('Affichage du tableau');
for (var val of liste) {
console.log(val);
}
console.timeEnd('Affichage du tableau');
// Remplissage du tableau: 2067.762ms
// Affichage du tableau: 238779.176ms
A noter également que for(... of …) devient même
problématique …
On a quasiment 10x le temps d’affichage (On fait
un reparcours du tableau à chaque itération vers
la fin).
Dénigrons !
A tester vous-même :
http://jsfiddle.net/jucrouzet/fhgkaLgt/
3.
Il n’est vraiment pas souple...
Dénigrons !
Une seul choix nous est offert : du début à la fin
du tableau / objet, si l’on souhaite itérer
différemment, on est obligés de “bricoler”.
Dénigrons !
var liste = [ 1, 2, 3, ....];
// Incrémenter de deux en deux ?
for (var i in liste) {
if (i % 2) {
// Mon code a éxecuter à 1 3 4 ...
}
// Autant écrire
// } else { ignorer cette mémoire gaspillée }
}
// Sinon, il faut faire la même chose lors de la
génération
Toute coquetterie est interdite, on se limite à :
Wow. Folie.
Sinon le code devient vite illisible.
Et donc, les générateurs !
Le générateur est un concept assez répandu
dans beaucoup d’autres langage.
Générateurs
Mainstream
label !
Imaginez qu’au lieu d’itérer sur une liste déjà
prête, vous allez générer des valeurs au fur et à
mesure (et en parallèle) de votre itération ...
Générateurs
Générateurs
Processus d’itération classique :
Génération d’
une liste
Stockage de la
liste
Itération dans
la liste
Liste
finie ?
Non
Oui
Générateurs
Processus d’itération classique :
Génération d’
une liste
Stockage de la
liste
Itération dans
la liste
Liste
finie ?
Non
Oui
Opération
synchrone/bloquante
Opération
synchrone/bloquante
Générateurs
Processus générateur :
Création d’un
générateur
Stockage du
générateur en
mémoire
Attente / Récup
prochain élément
dans la pile du
générateur
Générateur
Fini ?
Non
Oui
Génération d’
un élément de
la liste
Empiler l’élément
Fini ?
Non
Oui
En parallèle
Générateurs
Pour comprendre le “en parallèle”, voir
“Event Loop” dans :
http://slideshare.net/jucrouzet/promises-javascript
Générateurs
On va donc itérer non plus sur une liste
prédéfinie mais sur des valeurs que l’on peut
générer de la manière que l’on veut.
Générateurs
Le système de pile de données entre l’itération
et la génération gérera la synchronisation
lorsque l’un génère ou consomme plus vite que l’
autre.
Alors c’est parti !
Générateurs ECMAScript 6
Les générateurs en es6 sont définis grâce à l’
ajout d’une étoile (*) sur le mot-clé function
function* genereValues(arg1, arg2) {
//Genere mes valeurs.
}
// ou
var genereValues = function* (quelquechose) {
//Genere mes valeurs.
};
Générateurs ECMAScript 6
Un appel à cette “fonction” va donc retourner un
objet qui est un itérateur :
for (var valeur of genereValues(42, 'chihuahua')) {
// Faire quelque chose
}
Notez bien : for (... of …) et non for (... in …) !
Générateurs ECMAScript 6
Occupons-nous maintenant du corps de ce
générateur afin qu’il génère effectivement
quelque chose...
Générateurs ECMAScript 6
Le corps d’un générateur est identique à une
fonction normale
var generateur = function* (value) {
// Corps du générateur
var uneVariable = 42;
while(true) {
uneVariable += value;
}
// Fin corps du générateur
};
Générateurs ECMAScript 6
A la différence près qu’il ne retourne pas de
valeur mais qu’il en génère.
On utilise donc ici le le mot-clé yield (genère)
var generateur = function* (value) {
var uneVariable = 42;
while(true) {
uneVariable += value;
yield uneVariable;
}
};
Contrairement à return, yield ne vas pas
interrompre l’exécution de la fonction.
Générateurs ECMAScript 6
On a donc :
var generateur = function* (value) {
var uneVariable = 42;
while(true) {
uneVariable += value;
yield uneVariable;
}
};
for (var valeur of generateur(10)) {
console.log(valeur);
// 52 … 62 … 72 … 82 … (vers l’infini et au delà)
}
Générateurs ECMAScript 6
Comme vous pouvez le lire, l’itération va tourner en infini.
Ce n’est probablement pas ce que l’on veut.
var generateur = function* (value) {
var uneVariable = 42;
while(value >= 0) {
uneVariable += value;
yield uneVariable;
value--;
}
};
for (var valeur of generateur(5)) {
console.log(valeur);
// 47 … 51 … 54 … 56 … 57
}
Faire mumuse sur JSFiddle
Générateurs ECMAScript 6
Maintenant que nous avons fait notre premier générateur
(qui n’a rien de mieux qu’une itération) ; allons un peu plus
loin dans l’inspection de l’objet Generator ...
Générateurs ECMAScript 6
Inspectons le retour de generateur() dans l’
inspecteur d’une console JS :
var iterateur = generateur(10);
console.log(iterator);
>> Generator { }
// Nous avons donc bien objet de type Generator,
// ouvrons le avec l’inspecteur en utilisant les
// variables de substitution de console.log() :
console.log('%o', iterator);
Générateurs ECMAScript 6
Nous savons donc que Generator est une classe qui offre
deux méthodes publiques : next() et throw()
Générateurs ECMAScript 6
Comme son nom l’indique, next() est un appel à dépiler la
prochaine valeur générées par notre générateur.
Si elle est disponible, elle est retournée, sinon, on considère
la boucle finie.
Analysons le retour de Generator.next() ...
Générateurs ECMAScript 6
var generateurVide = function* () {};
var iterateurVide = generateurVide();
console.log(iterateurVide.next());
// >> Object { value: undefined, done: true }
var generateur = function* () {
yield 'AC/DC';
yield 'Iron Maiden';
yield 'Justin Bieber';
yield 'Metallica';
};
var iterateur = generateur();
console.log(iterateur.next());
console.log(iterateur.next());
console.log(iterateur.next());
console.log(iterateur.next());
console.log(iterateur.next());
// >> Object { value: "AC/DC", done: false }
// >> Object { value: "Iron Maiden", done: false }
// >> Object { value: "Justin Bieber", done: false }
// >> Object { value: "Metallica", done: false }
// >> Object { value: undefined, done: true }
Faire mumuse sur JSFiddle
Générateurs ECMAScript 6
Le fonctionnement des Generator dans une boucle
for (... of … ) est donc maintenant simple à
comprendre :
Chaque itération fait un appel à .next(), si l’objet de retour
a la propriété done à false, elles assignent la valeur de la
propriété value pour cette itération.
Si l’objet de retour a la propriété done à true, elles s’arrêtent
immédiatement.
Générateurs ECMAScript 6
Afin maintenant de comprendre comment la synchronisation
est faite, passons à un autre test.
Un test champêtre.
Générateurs ECMAScript 6
var generateurPoete = function* () {
console.log('Un Kalachnikov dans le boule en introduction');
yield 'b';
console.log('Ouest Side, Ouest Side, 92 injection');
yield '2o';
console.log('Certains croivent qu'ils rivalisent, faudra qu'on les
hospitalise');
yield 'b';
console.log('Tu tiens pas la route, pé-ra avec un "A" collé dans le dos');
yield 'a';
};
var neuf2_izi = generateurPoete();
console.log(neuf2_izi.next());
console.log(neuf2_izi.next());
console.log(neuf2_izi.next());
console.log(neuf2_izi.next());
console.log(neuf2_izi.next());
Faire mumuse sur JSFiddle
Donc le code suivant :
Générateurs ECMAScript 6
"Un Kalachnikov dans le boule en introduction"
Object { value: "b", done: false }
"Ouest Side, Ouest Side, 92 injection"
Object { value: "2o", done: false }
"Certains croient qu'ils rivalisent, faudra qu'on les
hospitalise"
Object { value: "b", done: false }
"Tu tiens pas la route, pé-ra avec un "A" collé dans le dos"
Object { value: "a", done: false }
Object { value: undefined, done: true }
Affichera dans la console :
Générateurs ECMAScript 6
Comme vient de nous le prouver Booba, l’instruction yield
empile une valeur puis mets en pause l’exécution du
générateur jusqu'à ce que .next() soit appelé pour la dépiler.
Générateurs ECMAScript 6
Cette technique présente deux avantages :
- La mémoire n’est pas allouée pour des tonnes de valeurs
mais uniquement pour la valeur en cours d’itération ;
- Les valeurs ne sont pas générées dans une boucle qui
utilise toute les ressources le temps de la génération mais
uniquement “à la demande”.
Générateurs ECMAScript 6
Un autre avantage des générateurs est que la
“communication” entre le Generator et son consommateur
(l'appelant de .next()) n’est pas unidirectionnelle, c’est un
dialogue !
Générateurs ECMAScript 6
La méthode .next() accèpte un argument, c’est la valeur qui
sera retournée par yield.
Donc si :
monGenerator.next('coucou');
alors dans le générateur :
var message = yield 'valeur générée';
// message est égale à ‘coucou’
Générateurs ECMAScript 6
Appliquons ça sur un exemple dramatique :
Générateurs ECMAScript 6
var patronat = function* () {
var tours = 3;
while(tours) {
var demande = yield 'rien';
window.console.log(
'Patron : Parce que la dernière fois vous avez déjà demandé %s',
demande
);
tours--;
}
};
var dialogueSocial = patronat();
var syndicat = function(demande) {
var reponse = dialogueSocial.next(demande);
if (reponse && !reponse.done && reponse.value) {
window.console.log('Syndicat : On a demandé %s et on a eu => %s', demande, reponse.
value);
} else {
window.console.log('Syndicat : On démarre une grève');
}
};
syndicat('une augmentation');
syndicat('des congés');
syndicat('des tickets resto');
syndicat('des RTT');
Faire mumuse sur JSFiddle
Générateurs ECMAScript 6
"Syndicat : On a demandé une augmentation et on a eu => rien"
"Patron : Parce que la dernière fois vous avez déjà demandé des
congés"
"Syndicat : On a demandé des congés et on a eu => rien"
"Patron : Parce que la dernière fois vous avez déjà demandé des
tickets resto"
"Syndicat : On a demandé des tickets resto et on a eu => rien"
"Patron : Parce que la dernière fois vous avez déjà demandé des
RTT"
"Syndicat : On démarre une grève"
ECMAScript 6, c’est un coup monté du patronat.
Je vois que ça.
Générateurs ECMAScript 6
Bon, ce dialogue a bien dérapé.
Les bugs ça arrive.
Tiens d’ailleurs on a pas parlé de l’autre méthode de
Generator, .throw().
Magnifique transition.
Générateurs ECMAScript 6
La méthode .throw() permet, toujours dans cet esprit de
dialogue constructif, au consommateur du générateur (celui
qui appèle .next(), par exemple) de lever une exception dans
le corps de la fonction du Generator, au niveau du yield.
Disons le sans détour, c’est un moyen de torpiller de l’
intérieur !
Heureusement, un gilet pare-balles nommé try/catch est là
pour gérer ça.
Générateurs ECMAScript 6
.throw() prend donc un argument, typiquement une chaîne de
caractère (message d’erreur), ou un objet Error.
Cette valeur sera lancée comme Exception lors du prochain
appel de yield.
Générateurs ECMAScript 6
function* monGenerateurQuonEmbete() {
var loops = 5;
while(loops) {
try {
yield loops;
} catch(err) {
console.log('Aie, je viens de recevoir %o', err); // 1 ->
return;
}
loops--;
}
};
var victime = monGenerateurQuonEmbete();
console.log(victime.next()); // 2 ->
console.log(victime.next()); // 3 ->
console.log(victime.throw(new Error('Et pan dans les dents'))); // 4 ->
console.log(victime.next()); // 5 ->
>> Object { value: 5, done: false } // <- 2
>> Object { value: 4, done: false } // <- 3
>> "Aie, je viens de recevoir Error: Et pan dans les dents [...]" // <- 1
>> Object { value: undefined, done: true } // <- 4
>> Object { value: undefined, done: true } // <- 5
Faire mumuse sur JSFiddle
Générateurs ECMAScript 6
Fin du premier chapitre.
Dans le prochain, on explorera comment coupler les
générateurs et les promesses.
Tout un programme.
Mais ça vaut vraiment le coup.
Promis.
Pour me contacter :

Contenu connexe

Tendances

Présentation JavaScript
Présentation JavaScriptPrésentation JavaScript
Présentation JavaScripttarkan_
 
Javascript pour les Développeurs WEB
Javascript pour les Développeurs WEBJavascript pour les Développeurs WEB
Javascript pour les Développeurs WEBAbbes Rharrab
 
Javascript : que fait ce code?
Javascript : que fait ce code?Javascript : que fait ce code?
Javascript : que fait ce code?Ruau Mickael
 
Javascript Json artchitecture
Javascript  Json artchitecture Javascript  Json artchitecture
Javascript Json artchitecture zaghir
 
CocoaHeads Rennes #1 : Grand Central Dispatch
CocoaHeads Rennes #1 : Grand Central DispatchCocoaHeads Rennes #1 : Grand Central Dispatch
CocoaHeads Rennes #1 : Grand Central DispatchCocoaHeadsRNS
 
Javascript pour le développeur Java
Javascript pour le développeur JavaJavascript pour le développeur Java
Javascript pour le développeur Javajollivetc
 
Function oop - bonnes pratiques ms tech days
Function   oop - bonnes pratiques ms tech daysFunction   oop - bonnes pratiques ms tech days
Function oop - bonnes pratiques ms tech daysJean-Pierre Vincent
 
Programmation Orientée Objet et les Traits en PHP 5.4
Programmation Orientée Objet et les Traits en PHP 5.4Programmation Orientée Objet et les Traits en PHP 5.4
Programmation Orientée Objet et les Traits en PHP 5.4halleck45
 
Python For Data Science - French Course
Python For Data Science - French CoursePython For Data Science - French Course
Python For Data Science - French CourseHaytam EL YOUSSFI
 
Patterns et bonnes pratiques autour de JavaScript
Patterns et bonnes pratiques autour de JavaScriptPatterns et bonnes pratiques autour de JavaScript
Patterns et bonnes pratiques autour de JavaScriptMicrosoft Technet France
 
Programmation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulationProgrammation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulationECAM Brussels Engineering School
 
Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)Dr Samir A. ROUABHI
 

Tendances (18)

Présentation JavaScript
Présentation JavaScriptPrésentation JavaScript
Présentation JavaScript
 
Javascript pour les Développeurs WEB
Javascript pour les Développeurs WEBJavascript pour les Développeurs WEB
Javascript pour les Développeurs WEB
 
Cours php
Cours phpCours php
Cours php
 
Promises Javascript
Promises JavascriptPromises Javascript
Promises Javascript
 
Javascript : que fait ce code?
Javascript : que fait ce code?Javascript : que fait ce code?
Javascript : que fait ce code?
 
Javascript proprement
Javascript proprementJavascript proprement
Javascript proprement
 
Javascript Json artchitecture
Javascript  Json artchitecture Javascript  Json artchitecture
Javascript Json artchitecture
 
CocoaHeads Rennes #1 : Grand Central Dispatch
CocoaHeads Rennes #1 : Grand Central DispatchCocoaHeads Rennes #1 : Grand Central Dispatch
CocoaHeads Rennes #1 : Grand Central Dispatch
 
Javascript pour le développeur Java
Javascript pour le développeur JavaJavascript pour le développeur Java
Javascript pour le développeur Java
 
Function oop - bonnes pratiques ms tech days
Function   oop - bonnes pratiques ms tech daysFunction   oop - bonnes pratiques ms tech days
Function oop - bonnes pratiques ms tech days
 
Introduction à Python
Introduction à PythonIntroduction à Python
Introduction à Python
 
Programmation Orientée Objet et les Traits en PHP 5.4
Programmation Orientée Objet et les Traits en PHP 5.4Programmation Orientée Objet et les Traits en PHP 5.4
Programmation Orientée Objet et les Traits en PHP 5.4
 
Python For Data Science - French Course
Python For Data Science - French CoursePython For Data Science - French Course
Python For Data Science - French Course
 
Patterns et bonnes pratiques autour de JavaScript
Patterns et bonnes pratiques autour de JavaScriptPatterns et bonnes pratiques autour de JavaScript
Patterns et bonnes pratiques autour de JavaScript
 
Programmation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulationProgrammation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulation
 
Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)
 
Part1
Part1Part1
Part1
 
La programmation fonctionnelle en javascript / PF
La programmation fonctionnelle en javascript / PFLa programmation fonctionnelle en javascript / PF
La programmation fonctionnelle en javascript / PF
 

Similaire à Javascript les générateurs (generators)

Support tutoriel : Créer votre jeu en HTML5
Support tutoriel : Créer votre jeu en HTML5Support tutoriel : Créer votre jeu en HTML5
Support tutoriel : Créer votre jeu en HTML5SmartnSkilled
 
Javascript - Fonctions : que fait ce code ?
Javascript - Fonctions : que fait ce code ?Javascript - Fonctions : que fait ce code ?
Javascript - Fonctions : que fait ce code ?Ruau Mickael
 
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...Normandy JUG
 
JavaScript pour les développeurs .NET
JavaScript pour les développeurs .NETJavaScript pour les développeurs .NET
JavaScript pour les développeurs .NETThomas Conté
 
Meetup#1 talk#1
Meetup#1 talk#1Meetup#1 talk#1
Meetup#1 talk#1neopixl
 
11-Concurrence-Section critiques.pdf
11-Concurrence-Section critiques.pdf11-Concurrence-Section critiques.pdf
11-Concurrence-Section critiques.pdfPatiento Del Mar
 
C# et .NET : Enigmes et puzzles
C# et .NET : Enigmes  et puzzlesC# et .NET : Enigmes  et puzzles
C# et .NET : Enigmes et puzzlesMicrosoft
 
Future of java script web version
Future of java script web versionFuture of java script web version
Future of java script web versionSébastien Pertus
 
Initiation au code : Ateliers en C# (applications desktop et mobile native)
Initiation au code : Ateliers en C# (applications desktop et mobile native)Initiation au code : Ateliers en C# (applications desktop et mobile native)
Initiation au code : Ateliers en C# (applications desktop et mobile native)Stéphanie Hertrich
 
BEBUTANT JAVA
BEBUTANT  JAVABEBUTANT  JAVA
BEBUTANT JAVAviolonjo
 
Cours c#
Cours c#Cours c#
Cours c#zan
 
Librairies Java qui changent la vie
Librairies Java qui changent la vieLibrairies Java qui changent la vie
Librairies Java qui changent la viecluelessjoe
 
Cours j query-id1575
Cours j query-id1575Cours j query-id1575
Cours j query-id1575kate2013
 
Terraform OpenStack : Mise en pratique sur infrastructure OVH (Rennes devops)
Terraform OpenStack : Mise en pratique sur infrastructure OVH (Rennes devops) Terraform OpenStack : Mise en pratique sur infrastructure OVH (Rennes devops)
Terraform OpenStack : Mise en pratique sur infrastructure OVH (Rennes devops) Joël Séguillon
 

Similaire à Javascript les générateurs (generators) (20)

Support tutoriel : Créer votre jeu en HTML5
Support tutoriel : Créer votre jeu en HTML5Support tutoriel : Créer votre jeu en HTML5
Support tutoriel : Créer votre jeu en HTML5
 
Javascript - Fonctions : que fait ce code ?
Javascript - Fonctions : que fait ce code ?Javascript - Fonctions : que fait ce code ?
Javascript - Fonctions : que fait ce code ?
 
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
 
JavaScript pour les développeurs .NET
JavaScript pour les développeurs .NETJavaScript pour les développeurs .NET
JavaScript pour les développeurs .NET
 
Meetup#1 talk#1
Meetup#1 talk#1Meetup#1 talk#1
Meetup#1 talk#1
 
11-Concurrence-Section critiques.pdf
11-Concurrence-Section critiques.pdf11-Concurrence-Section critiques.pdf
11-Concurrence-Section critiques.pdf
 
C# et .NET : Enigmes et puzzles
C# et .NET : Enigmes  et puzzlesC# et .NET : Enigmes  et puzzles
C# et .NET : Enigmes et puzzles
 
Future of java script web version
Future of java script web versionFuture of java script web version
Future of java script web version
 
Initiation au code : Ateliers en C# (applications desktop et mobile native)
Initiation au code : Ateliers en C# (applications desktop et mobile native)Initiation au code : Ateliers en C# (applications desktop et mobile native)
Initiation au code : Ateliers en C# (applications desktop et mobile native)
 
BEBUTANT JAVA
BEBUTANT  JAVABEBUTANT  JAVA
BEBUTANT JAVA
 
Vert.x 3
Vert.x 3Vert.x 3
Vert.x 3
 
C++ 11/14
C++ 11/14C++ 11/14
C++ 11/14
 
Ruby Pour RoR
Ruby Pour RoRRuby Pour RoR
Ruby Pour RoR
 
Implementing a key/value store
Implementing a key/value storeImplementing a key/value store
Implementing a key/value store
 
Cours c#
Cours c#Cours c#
Cours c#
 
Librairies Java qui changent la vie
Librairies Java qui changent la vieLibrairies Java qui changent la vie
Librairies Java qui changent la vie
 
Cours j query-id1575
Cours j query-id1575Cours j query-id1575
Cours j query-id1575
 
Terraform OpenStack : Mise en pratique sur infrastructure OVH (Rennes devops)
Terraform OpenStack : Mise en pratique sur infrastructure OVH (Rennes devops) Terraform OpenStack : Mise en pratique sur infrastructure OVH (Rennes devops)
Terraform OpenStack : Mise en pratique sur infrastructure OVH (Rennes devops)
 
Algo poo ts
Algo poo tsAlgo poo ts
Algo poo ts
 
Tp-jquery
Tp-jqueryTp-jquery
Tp-jquery
 

Javascript les générateurs (generators)

  • 2. Préambule ... Nous allons parler ici de fonctionnalités qui ne sont disponibles que depuis ECMAScript 6. Leurs support n’est pas généralisé ; par exemple pas de IE, ou bien pas de Node.js (sauf si activation de --harmony ) http://kangax.github.io/compat-table/es6/#generators https://github.com/google/traceur-compiler
  • 4. En programmation, nous utilisons des boucles. Itérateurs ? for (var i = 0; i < 10; i++) { // Faire quelque chose 10 fois. } while(condition) { // Faire quelque chose jusqu'à ce que ... }
  • 5. Le principe est de répéter l'exécution d’un code un certain nombre de fois. Chaque “tour” de notre boucle est une itération. Parcourir cette boucle se dit donc itérer. Itérateurs ?
  • 6. Il est aussi possible d’itérer sur un tableau : Itérateurs ? var valeurs = [ 'a', 'b', 'c' ]; // Itérer sur les indices du tableau : for (var indice in valeurs) { console.log(valeurs[indice ]); } // Ou utiliser la méthode Array.forEach() valeurs.forEach(function(valeur) { console.log(valeur); });
  • 7. Ou un objet : Itérateurs ? var valeurs = {a : 1, b : 2, c : 3 }; // Itérer sur les propriétés : for (var indice in valeurs) { if (valeurs.hasOwnProperty(indice)) { console.log( '[%s] => %s', indice, valeurs[indice] ); } }
  • 8. On considère alors que les tableaux et les objets sont des itérateurs. Mais comme on vient de le voir, on itère sur les indices ou propriétés. C’est pas très pratique. Itérateurs ?
  • 9. ECMAScript 6 (es6) introduit une nouvelle syntaxe d’itération : for ( … of …) : Itérateurs ? var valeurs = [3, 4, 5] ; for (var indice in valeurs) { console.log(indice); } // 0 ... 1 ... 2 for (var valeur of valeurs) { console.log(valeur); } // 3 ... 4 ... 5
  • 10. o/ joie, bonheur, alacrité nous pouvons donc désormais itérer sur des tableaux et objets sans recoder la roue ou utiliser des Frameworks (jQuery $.each, par exemple). Itérateurs ?
  • 11. Seulement, dehors il y a le vrai monde... Itérateurs ? Et le vrai monde, il veut itérer, et si possible de manière optimisée et pas que sur des tableaux et des objets. Petit interlude philosophie, optionnel. Cliquez pour visionner.
  • 13. L’algorithme consistant à remplir un tableau/objet puis d’itérer dessus a beau avoir fait la route avec nous depuis nos premières pages, il présente de sérieux défauts. Dénigrons !
  • 14. 1. Il veut même pas rendre ses ressources. Radin. Dénigrons ! Le tableau/objet est généré puis stocké en mémoire entièrement pendant toute l’itération de notre boucle, même si nous n’avons plus besoin de ces valeurs.
  • 15. Dénigrons ! // Création de ma liste. var liste = []; // Remplissage for (var i = 0; i < 1000000; i++) { liste.push(Math.random()); } // Affichage for (var val of liste) { console.log(val); } Si l’on considère le code suivant : L’importante partie de mémoire utilisée pour stocker les 1 million de valeurs va rester allouée pendant toute l’itération de la boucle ; même si nous n’avons plus besoin de ces valeurs après le console.log().
  • 16. 2. Il a la manie de tout bloquer. Dénigrons ! Pendant toute la création puis l’itération du tableau/objet, le navigateur ou le processus est “occupé”. Cela peut retarder la gestion d’ évènements, ralentir des affichages et rendre instable le navigateur/programme.
  • 17. Dénigrons ! // Création de ma liste. var liste = []; // Remplissage console.time('Remplissage du tableau'); for (var i = 0; i < 1000000; i++) { liste.push(Math.random()); } console.timeEnd('Remplissage du tableau'); // Affichage console.time('Affichage du tableau'); for (var val in liste) { console.log(val); } console.timeEnd('Affichage du tableau'); // Remplissage du tableau: 2054.109ms // Affichage du tableau: 2185.662ms Ajoutons un timer à notre code : Ici, on voit que l’on a été “bloqué” pendant deux fois 2 secondes. Faire mumuse sur JSFiddle
  • 18. Dénigrons ! // Remplissage console.time('Remplissage du tableau'); for (var i = 0; i < 1000000; i++) { liste.push(Math.random()); } console.timeEnd('Remplissage du tableau'); // Affichage console.time('Affichage du tableau'); for (var val of liste) { console.log(val); } console.timeEnd('Affichage du tableau'); // Remplissage du tableau: 2067.762ms // Affichage du tableau: 238779.176ms A noter également que for(... of …) devient même problématique … On a quasiment 10x le temps d’affichage (On fait un reparcours du tableau à chaque itération vers la fin).
  • 19. Dénigrons ! A tester vous-même : http://jsfiddle.net/jucrouzet/fhgkaLgt/
  • 20. 3. Il n’est vraiment pas souple... Dénigrons ! Une seul choix nous est offert : du début à la fin du tableau / objet, si l’on souhaite itérer différemment, on est obligés de “bricoler”.
  • 21. Dénigrons ! var liste = [ 1, 2, 3, ....]; // Incrémenter de deux en deux ? for (var i in liste) { if (i % 2) { // Mon code a éxecuter à 1 3 4 ... } // Autant écrire // } else { ignorer cette mémoire gaspillée } } // Sinon, il faut faire la même chose lors de la génération Toute coquetterie est interdite, on se limite à : Wow. Folie. Sinon le code devient vite illisible.
  • 22. Et donc, les générateurs !
  • 23. Le générateur est un concept assez répandu dans beaucoup d’autres langage. Générateurs Mainstream label !
  • 24. Imaginez qu’au lieu d’itérer sur une liste déjà prête, vous allez générer des valeurs au fur et à mesure (et en parallèle) de votre itération ... Générateurs
  • 25. Générateurs Processus d’itération classique : Génération d’ une liste Stockage de la liste Itération dans la liste Liste finie ? Non Oui
  • 26. Générateurs Processus d’itération classique : Génération d’ une liste Stockage de la liste Itération dans la liste Liste finie ? Non Oui Opération synchrone/bloquante Opération synchrone/bloquante
  • 27. Générateurs Processus générateur : Création d’un générateur Stockage du générateur en mémoire Attente / Récup prochain élément dans la pile du générateur Générateur Fini ? Non Oui Génération d’ un élément de la liste Empiler l’élément Fini ? Non Oui En parallèle
  • 28. Générateurs Pour comprendre le “en parallèle”, voir “Event Loop” dans : http://slideshare.net/jucrouzet/promises-javascript
  • 29. Générateurs On va donc itérer non plus sur une liste prédéfinie mais sur des valeurs que l’on peut générer de la manière que l’on veut.
  • 30. Générateurs Le système de pile de données entre l’itération et la génération gérera la synchronisation lorsque l’un génère ou consomme plus vite que l’ autre.
  • 32. Générateurs ECMAScript 6 Les générateurs en es6 sont définis grâce à l’ ajout d’une étoile (*) sur le mot-clé function function* genereValues(arg1, arg2) { //Genere mes valeurs. } // ou var genereValues = function* (quelquechose) { //Genere mes valeurs. };
  • 33. Générateurs ECMAScript 6 Un appel à cette “fonction” va donc retourner un objet qui est un itérateur : for (var valeur of genereValues(42, 'chihuahua')) { // Faire quelque chose } Notez bien : for (... of …) et non for (... in …) !
  • 34. Générateurs ECMAScript 6 Occupons-nous maintenant du corps de ce générateur afin qu’il génère effectivement quelque chose...
  • 35. Générateurs ECMAScript 6 Le corps d’un générateur est identique à une fonction normale var generateur = function* (value) { // Corps du générateur var uneVariable = 42; while(true) { uneVariable += value; } // Fin corps du générateur };
  • 36. Générateurs ECMAScript 6 A la différence près qu’il ne retourne pas de valeur mais qu’il en génère. On utilise donc ici le le mot-clé yield (genère) var generateur = function* (value) { var uneVariable = 42; while(true) { uneVariable += value; yield uneVariable; } }; Contrairement à return, yield ne vas pas interrompre l’exécution de la fonction.
  • 37. Générateurs ECMAScript 6 On a donc : var generateur = function* (value) { var uneVariable = 42; while(true) { uneVariable += value; yield uneVariable; } }; for (var valeur of generateur(10)) { console.log(valeur); // 52 … 62 … 72 … 82 … (vers l’infini et au delà) }
  • 38. Générateurs ECMAScript 6 Comme vous pouvez le lire, l’itération va tourner en infini. Ce n’est probablement pas ce que l’on veut. var generateur = function* (value) { var uneVariable = 42; while(value >= 0) { uneVariable += value; yield uneVariable; value--; } }; for (var valeur of generateur(5)) { console.log(valeur); // 47 … 51 … 54 … 56 … 57 } Faire mumuse sur JSFiddle
  • 39. Générateurs ECMAScript 6 Maintenant que nous avons fait notre premier générateur (qui n’a rien de mieux qu’une itération) ; allons un peu plus loin dans l’inspection de l’objet Generator ...
  • 40. Générateurs ECMAScript 6 Inspectons le retour de generateur() dans l’ inspecteur d’une console JS : var iterateur = generateur(10); console.log(iterator); >> Generator { } // Nous avons donc bien objet de type Generator, // ouvrons le avec l’inspecteur en utilisant les // variables de substitution de console.log() : console.log('%o', iterator);
  • 41. Générateurs ECMAScript 6 Nous savons donc que Generator est une classe qui offre deux méthodes publiques : next() et throw()
  • 42. Générateurs ECMAScript 6 Comme son nom l’indique, next() est un appel à dépiler la prochaine valeur générées par notre générateur. Si elle est disponible, elle est retournée, sinon, on considère la boucle finie. Analysons le retour de Generator.next() ...
  • 43. Générateurs ECMAScript 6 var generateurVide = function* () {}; var iterateurVide = generateurVide(); console.log(iterateurVide.next()); // >> Object { value: undefined, done: true } var generateur = function* () { yield 'AC/DC'; yield 'Iron Maiden'; yield 'Justin Bieber'; yield 'Metallica'; }; var iterateur = generateur(); console.log(iterateur.next()); console.log(iterateur.next()); console.log(iterateur.next()); console.log(iterateur.next()); console.log(iterateur.next()); // >> Object { value: "AC/DC", done: false } // >> Object { value: "Iron Maiden", done: false } // >> Object { value: "Justin Bieber", done: false } // >> Object { value: "Metallica", done: false } // >> Object { value: undefined, done: true } Faire mumuse sur JSFiddle
  • 44. Générateurs ECMAScript 6 Le fonctionnement des Generator dans une boucle for (... of … ) est donc maintenant simple à comprendre : Chaque itération fait un appel à .next(), si l’objet de retour a la propriété done à false, elles assignent la valeur de la propriété value pour cette itération. Si l’objet de retour a la propriété done à true, elles s’arrêtent immédiatement.
  • 45. Générateurs ECMAScript 6 Afin maintenant de comprendre comment la synchronisation est faite, passons à un autre test. Un test champêtre.
  • 46. Générateurs ECMAScript 6 var generateurPoete = function* () { console.log('Un Kalachnikov dans le boule en introduction'); yield 'b'; console.log('Ouest Side, Ouest Side, 92 injection'); yield '2o'; console.log('Certains croivent qu'ils rivalisent, faudra qu'on les hospitalise'); yield 'b'; console.log('Tu tiens pas la route, pé-ra avec un "A" collé dans le dos'); yield 'a'; }; var neuf2_izi = generateurPoete(); console.log(neuf2_izi.next()); console.log(neuf2_izi.next()); console.log(neuf2_izi.next()); console.log(neuf2_izi.next()); console.log(neuf2_izi.next()); Faire mumuse sur JSFiddle Donc le code suivant :
  • 47. Générateurs ECMAScript 6 "Un Kalachnikov dans le boule en introduction" Object { value: "b", done: false } "Ouest Side, Ouest Side, 92 injection" Object { value: "2o", done: false } "Certains croient qu'ils rivalisent, faudra qu'on les hospitalise" Object { value: "b", done: false } "Tu tiens pas la route, pé-ra avec un "A" collé dans le dos" Object { value: "a", done: false } Object { value: undefined, done: true } Affichera dans la console :
  • 48. Générateurs ECMAScript 6 Comme vient de nous le prouver Booba, l’instruction yield empile une valeur puis mets en pause l’exécution du générateur jusqu'à ce que .next() soit appelé pour la dépiler.
  • 49. Générateurs ECMAScript 6 Cette technique présente deux avantages : - La mémoire n’est pas allouée pour des tonnes de valeurs mais uniquement pour la valeur en cours d’itération ; - Les valeurs ne sont pas générées dans une boucle qui utilise toute les ressources le temps de la génération mais uniquement “à la demande”.
  • 50. Générateurs ECMAScript 6 Un autre avantage des générateurs est que la “communication” entre le Generator et son consommateur (l'appelant de .next()) n’est pas unidirectionnelle, c’est un dialogue !
  • 51. Générateurs ECMAScript 6 La méthode .next() accèpte un argument, c’est la valeur qui sera retournée par yield. Donc si : monGenerator.next('coucou'); alors dans le générateur : var message = yield 'valeur générée'; // message est égale à ‘coucou’
  • 52. Générateurs ECMAScript 6 Appliquons ça sur un exemple dramatique :
  • 53. Générateurs ECMAScript 6 var patronat = function* () { var tours = 3; while(tours) { var demande = yield 'rien'; window.console.log( 'Patron : Parce que la dernière fois vous avez déjà demandé %s', demande ); tours--; } }; var dialogueSocial = patronat(); var syndicat = function(demande) { var reponse = dialogueSocial.next(demande); if (reponse && !reponse.done && reponse.value) { window.console.log('Syndicat : On a demandé %s et on a eu => %s', demande, reponse. value); } else { window.console.log('Syndicat : On démarre une grève'); } }; syndicat('une augmentation'); syndicat('des congés'); syndicat('des tickets resto'); syndicat('des RTT'); Faire mumuse sur JSFiddle
  • 54. Générateurs ECMAScript 6 "Syndicat : On a demandé une augmentation et on a eu => rien" "Patron : Parce que la dernière fois vous avez déjà demandé des congés" "Syndicat : On a demandé des congés et on a eu => rien" "Patron : Parce que la dernière fois vous avez déjà demandé des tickets resto" "Syndicat : On a demandé des tickets resto et on a eu => rien" "Patron : Parce que la dernière fois vous avez déjà demandé des RTT" "Syndicat : On démarre une grève" ECMAScript 6, c’est un coup monté du patronat. Je vois que ça.
  • 55. Générateurs ECMAScript 6 Bon, ce dialogue a bien dérapé. Les bugs ça arrive. Tiens d’ailleurs on a pas parlé de l’autre méthode de Generator, .throw(). Magnifique transition.
  • 56. Générateurs ECMAScript 6 La méthode .throw() permet, toujours dans cet esprit de dialogue constructif, au consommateur du générateur (celui qui appèle .next(), par exemple) de lever une exception dans le corps de la fonction du Generator, au niveau du yield. Disons le sans détour, c’est un moyen de torpiller de l’ intérieur ! Heureusement, un gilet pare-balles nommé try/catch est là pour gérer ça.
  • 57. Générateurs ECMAScript 6 .throw() prend donc un argument, typiquement une chaîne de caractère (message d’erreur), ou un objet Error. Cette valeur sera lancée comme Exception lors du prochain appel de yield.
  • 58. Générateurs ECMAScript 6 function* monGenerateurQuonEmbete() { var loops = 5; while(loops) { try { yield loops; } catch(err) { console.log('Aie, je viens de recevoir %o', err); // 1 -> return; } loops--; } }; var victime = monGenerateurQuonEmbete(); console.log(victime.next()); // 2 -> console.log(victime.next()); // 3 -> console.log(victime.throw(new Error('Et pan dans les dents'))); // 4 -> console.log(victime.next()); // 5 -> >> Object { value: 5, done: false } // <- 2 >> Object { value: 4, done: false } // <- 3 >> "Aie, je viens de recevoir Error: Et pan dans les dents [...]" // <- 1 >> Object { value: undefined, done: true } // <- 4 >> Object { value: undefined, done: true } // <- 5 Faire mumuse sur JSFiddle
  • 59. Générateurs ECMAScript 6 Fin du premier chapitre. Dans le prochain, on explorera comment coupler les générateurs et les promesses. Tout un programme. Mais ça vaut vraiment le coup. Promis. Pour me contacter :