GRATUIT

Vos offres d'emploi informatique

Développeurs, chefs de projets, ingénieurs, informaticiens
Postez gratuitement vos offres d'emploi ici visibles par 4 000 000 de visiteurs uniques par mois

emploi.developpez.com

Découvrez le yielded style ou développement JavaScript asynchrone linéaire

Le , par Lcf.vs, Membre éprouvé
Si vous avez déjà fait du JavaScript asynchrone, vous avez certainement connaissance de techniques de traitement de la réponse.

Les callbacks

Ils vous permettent de traiter une réponse après l'exécution d'une fonction asynchrone, en cas d'erreur ou non.

Ils ont pour défauts de devoir avoir des fonctions gérant l'échec ou la réussite et de pousser vers un développement plein de fonctions imbriquées, nuisant à sa lisibilité et l'espace des arguments de votre fonction est pollué par les callbacks.

Pour palier cela, une autre méthode est apparue, les promesses (promises, en anglais).

Les promises

Les promesses ont une structure permettant aussi de gérer l'échec ou la réussite de l'exécution d'une fonction, d'exécuter des fonctions pendant la progression de l'exécution, etc.

Elles se différencient par le fait qu'une promesse est un objet auquel on passe tout un tas de fonctions, via ses méthodes, afin de gérer les différents états de l'exécution.

On se retrouve donc avec énormément de fonctions dans son code avec, parfois, des références internes.

Cela a tendance à déstructurer votre code de telle sorte que sa relecture peut devenir une vraie gymnastique cérébrale.

Le mot-clé yield

Me tenant à jour concernant les avancées du JavaScript, j'ai découvert, parmi les propositions de la future norme ECMAScript 6, le mot-clé yield.

Ce mot-clé, une sorte de return particulier, permet de créer des fonctions retournant un générateur, comprenez par là que la liste d'instructions que contient votre fonction fera un arrêt à chaque yield qu'elle contient, jusqu'à ce que vous appeliez le yield suivant, via la méthode next().

Exemple :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
var generate, generator; 
 
generate = function generate(value) { 
    yield 'Hello ' + value; 
}; 
 
generator = generate('World'); 
 
// on appelle le yield suivant du générateur, après 2 secondes 
setTimeout(function () { 
    // renverra "Hello World" dans votre console, après les 2 secondes 
    console.log(generator.next()); 
}, 2000);
Ces générateurs ont aussi une une méthode send().

Celle-ci permet d'envoyer une liste de valeurs au générateur, à un moment donné pendant le parcours des yield qu'il contient.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var generate, generator; 
 
generate = function generate() { 
    var message, response; 
 
    message = 'Hello '; 
    response = yield message; 
    yield message + response; 
}; 
 
generator = generate(); 
 
// on démarre le générateur, affichera "Hello " 
console.log(generator.next()); 
 
// on appelle le yield suivant du générateur, après 2 secondes 
setTimeout(function () { 
    // renverra "Hello World" dans votre console, après les 2 secondes 
    console.log(generator.send('World')); 
}, 2000);
Enfin, sachez que les générateurs ont aussi une méthode close(), afin de libérer la mémoire.

Le yielded style programming

Partant de cette découverte, je me suis dit qu'il devrait être possible d'affecter à une variable du contexte (scope) courant, le résultat d'une fonction asynchrone et d'ensuite poursuivre le processus.

C'est ainsi qu'est né yld (prononcez yielded).

Il s'agit d'un outil vous permettant de transformer un générateur en une liste d'instructions s'exécutant l'une après l'autre, comme s'il s'agissait d'une simple fonction mais attendant la réponse de fonctions asynchrones, quand c'est nécessaire.

De plus, il ajoute une notion de relation entre les différents scopes.

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var asyncFn1, asyncFn2; 
  
asyncFn1 = yld(function (a, b) { 
    var child, response; 
     
    if (isNaN(a) || isNaN(b)) { 
        // stoppe le processus immédiatement et renvoie une erreur avec le message spécifié 
        this.error = 'Invalid values'; 
    } 
     
    // this.yld est utilisable comme yld 
    child = yield this.yld(asyncFn2)(a); 
     
    response = yield child.send(b); 
     
    console.log(response); // 3 
}); 
 
asyncFn2 = function (a) { 
    var b, parent; 
     
    parent = this.parent; 
     
    // retourne le scope courant au scope parent 
    b = yield parent.send(this); 
     
    // retourne la réponse au scope parent 
    yield setTimeout(function () { 
        parent.send(a + b); 
    }, 3000); 
}; 
 
asyncFn1(1, 2);
Comme vous pouvez le constater, il n'y a que très peu de fonctions, vous passez uniquement les arguments dont vos fonctions ont besoin et, surtout, le processus s'interrompt à chaque yield, vous permettant de récupérer une valeur sur la même ligne que l'appel à une fonction asynchrone, comme si elle ne l'était pas.

N.B. : Le mot-clé yield étant une possibilité d'amélioration future du JavaScript, il est possible que yld ne s'exécute pas encore partout, néanmoins, yld est déjà conçu pour pouvoir s'exécuter en navigateur et sous Node.js.

Pour tester les différents exemples, je vous recommande l'utilisation d'un Firefox à jour.

EDIT : Le plus facile, pour vos tests, c'est via la console de Firebug.

Sinon, vous pouvez l'embarquer, dans votre HTML via
Code : Sélectionner tout
<script type="application/javascript;version=1.7"></script>
Où le 1.7 est, évidemment, la version minimale.

Source : https://github.com/Lcfvs/yld

Malgré l'habitude évidente qu'il vous faudra pour complètement en tirer avantage, trouvez-vous que cela peut réellement améliorer la lisibilité de votre code?
Sinon, qu'y reprochez-vous?


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse

Avatar de SylvainPV SylvainPV - Rédacteur/Modérateur https://www.developpez.com
le 08/08/2013 à 20:18
Cela fait des mois que je lis des articles sur ce nouvel opérateur miracle et je n'ai toujours trouvé aucun exemple concret qui me convainc de son intérêt. A priori son seul rôle est de gagner en lisibilité, seulement il m'a fallu plusieurs minutes pour comprendre ce que faisait le code en exemple. Tout ça pour me dire au final que j'aurais pu le réécrire en trois fois plus court et plus lisible avec des callbacks classiques.

Je devrais être un habitué du "callback hell" vu que je travaille régulièrement avec Node.js. Et pourtant cela ne m'a jamais posé aucun problème de lisibilité. Il suffit de suivre comme convention de toujours passer l'erreur en premier argument des fonctions, et de déclarer les fonctions nominativement et dans le même scope plutôt que de les imbriquer.

Est-ce que l'auteur aurait un exemple de problème résolu via les trois options: callbacks, promises et yield ; et qui mette bien en évidence les avantages de ce dernier. C'est ce qui manquait dans tous les articles que j'ai pu lire sur le sujet...
Avatar de Lcf.vs Lcf.vs - Membre éprouvé https://www.developpez.com
le 08/08/2013 à 22:04
@ SylvainPV :

Merci pour ce premier avis, tu trouveras donc les exemples demandés ci-dessous.

Le callback :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 
var handler1, handler2, handler3; 
 
handler1 = function handler1(value, callback) { 
    var error, newValue; 
 
    if (isNaN(value)) { 
        error = new Error('value isn\'t a number'); 
    } else { 
        newValue = value + 1; 
    } 
     
    callback(error, newValue, handler3); 
}; 
 
handler2 = function handler2(error, value, callback) { 
    var newError, newValue; 
 
    if (error) { 
        newError = error; 
    } else if (parseInt(value) !== value) { 
        error = new Error('value isn\'t an integer'); 
    } else { 
        newValue = value + 1; 
    } 
    callback(newError, newValue); 
}; 
 
handler3 = function handler3(error, value) { 
    if (error) { 
        throw error; 
    } else { 
        console.log(value); 
    } 
}; 
 
handler1(1, handler2);
Comme on peut le voir, on pollue les arguments avec une éventuelle erreur, que l'on doit à chaque fois tester, soi-même, dans le callback.

La promise :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 
var defer, handler1, errorHandler1, handler2, errorHandler2, handler3, promise; 
 
defer = require("promise").defer; 
 
handler1 = function handler1(value) { 
    var deferred; 
 
    deferred = defer(); 
 
    if (isNaN(value)) { 
        deferred.reject(); 
    } else { 
        deferred.resolve(value + 1); 
    } 
    return deferred.promise; 
}; 
 
errorHandler1 = function errorHandler1(error) { 
    throw new Error('value isn\'t a number'); 
}; 
 
handler2 = function handler2(value) { 
    var deferred; 
 
    deferred = defer(); 
 
    if (parseInt(value) !== value) { 
        deferred.reject(); 
    } else { 
        deferred.resolve(value + 1); 
    } 
    return deferred.promise; 
}; 
 
errorHandler2 = function errorHandler2(error) { 
    throw new Error('value isn\'t an integer'); 
}; 
 
handler3 = function handler3(value) { 
    console.log(value); 
}; 
 
promise = handler1(1); 
 
promise.then(handler2, errorHandler1) 
       .then(handler3, errorHandler2);
Les arguments sont plus propres, mais on remarque la création de beaucoup plus de fonctions, dans son code.

De plus, vous devez, vous-même, gérer le defer et retourner la promise.

La yielded :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
var handler1, handler2, handler3, yielded; 
 
handler1 = function handler1(value, handler2, handler3) { 
    var child, response; 
     
    if (isNaN(value)) { 
        this.error = 'value isn\'t a number'; 
    } 
 
    child = yield this.yld(handler2)(value + 1); 
 
    response = yield child.send(); 
 
    yield handler3(response); 
}; 
 
handler2 = function handler2(value) { 
    var parent; 
     
    if (parseInt(value) !== value) { 
        this.error = 'value isn\'t an integer'; 
    } 
     
    parent = this.parent; 
 
    yield parent.send(this); 
 
    yield parent.send(value + 1); 
}; 
 
handler3 = function handler3(value) { 
    console.log(value); 
}; 
 
yielded = yld(handler1); 
 
yielded(1, handler2, handler3);
Comme on peut le voir, on déclare aussi peu de fonctions qu'avec la méthode du callback, le premier handler gère chacune des réponses, lui-même, et une relation existe entre les différents handlers, permettant au processus enfant de communiquer avec le processus parent et inversement.

Pour comprendre ce que fait le code, il suffit donc simplement d'observer ce qui se passe dans le premier handler.

Enfin, contrairement aux promises, il devient très aisé, pour le premier handler (dans l'exemple), de savoir quand chacun des autres processus finit.
Avatar de SylvainPV SylvainPV - Rédacteur/Modérateur https://www.developpez.com
le 09/08/2013 à 0:32
Merci d'avoir pris le temps de rédiger ces exemples, néanmoins j'aurais espéré un exemple se rapprochant davantage d'une situation réelle. Ici l'opération est synchrone et on ne voit pas trop quel est le but recherché, entre l'incrémentation, le test isNaN puis isInteger...

Aussi, dans l'exemple avec les callbacks, il n'est pas obligatoire de tester dans chaque fonction une éventuelle erreur. C'est comme avec les exceptions, on n'est pas obligé de mettre une instruction try {} catch {} à chaque fois qu'on throw une exception. Si elle n'est pas catchée, elle remonte à la branche parente. Ici c'est la même chose, on transmet les erreurs aux fonctions appelantes et on traite chacune d'entre elles à l'endroit le plus adéquat.

Si on prend un exemple plus courant, par exemple avec un event callback + une requête AJAX + un timeout:

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var form = document.querySelector('form'); 
form.onsubmit = function(submitEvent) { 
  var name = document.querySelector('input').value;   
  request({ 
    uri: "http://example.com/upload", 
    body: name, 
    method: "POST" 
  }, function(err, response) { 
      if(err != null){ 
           showNotification("Server down, trying again in 5 seconds"); 
           setTimeout(function(){ 
               form.submit(); 
           }, 5000); 
      } else { 
           showNotification("Upload done : "+response); 
      } 
 }); 
 return false; 
};
; il y a effectivement un problème de lisibilité. Mais en isolant simplement les fonctions, c'est déjà beaucoup plus clair :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function onFormSubmit(submitEvent) { 
  var name = document.querySelector('input').value;   
  request({ 
    uri: "http://example.com/upload", 
    body: name, 
    method: "POST" 
  }, onUploadComplete); 
  return false; 
} 
   
function onUploadComplete(err, response) { 
  if(err != null){ 
	showNotification("Server down, trying again in 5 seconds"); 
	setTimeout(onFormSubmit, 5000); 
  } else { 
	showNotification("Upload done : "+response); 
  } 
} 
 
var form = document.querySelector('form'); 
form.onsubmit = onFormSubmit;
Comme tu le soulignes, l'opérateur yield permet de retrouver une "notation synchrone" afin d'écrire les appels à la fois de handler2 et handler3 dans handler1, autrement dit l'appel du callback au même niveau que l'appel du callback du callback. Mais je ne suis pas d'accord quand tu dis
Pour comprendre ce que fait le code, il suffit donc simplement d'observer ce qui se passe dans le premier handler.

; je mets au défi quiconque de me dire ce que fait le code en exemple sans regarder handler2 et handler3.

La programmation synchrone est appréciée car elle se lit comme un texte, de haut en bas. Mais les programmes n'ont pas une chronologie fixe et un scénario à sens unique. En particulier pour la programmation orientée objet ou orientée évènement: c'est prévu pour partir dans tous les sens ! Et je ne comprends pas l'intérêt de s'efforcer à lutter contre ça, c'est inexorable. Je ne crois pas qu'il faille adapter le langage, mais plutôt adapter notre manière de le lire. Plutôt que de parcourir un code en le défilant à la molette de la souris, on a qu'à utiliser davantage les raccourcis de navigation de certains IDE comme "Aller à la déclaration de cette fonction" ou "Trouver les références de cette fonction".
Avatar de Lcf.vs Lcf.vs - Membre éprouvé https://www.developpez.com
le 09/08/2013 à 0:46
Citation Envoyé par SylvainPV  Voir le message
Comme vous le soulignez, l'opérateur yield permet de retrouver une "notation synchrone" afin d'écrire les appels à la fois de handler2 et handler3 dans handler1, autrement dit l'appel du callback au même niveau que l'appel du callback du callback. Mais je ne suis pas d'accord quand vous dites
; je mets au défi quiconque de me dire ce que fait le code en exemple sans regarder handler2 et handler3.

Il est évident que qu'il faut un minimum savoir ce que font les autres handlers, sinon, ils n'auraient pas de raison d'être.

Néanmoins, si dans notre hander1, on appelle un handler qui ne fait que lire un fichier, de manière asynchrone (par exemple), et retourner le résultat, pour faire le traitement dans le handler1, les appels via this.yld() pourraient, idéalement, ne servir qu'à l'utilitaire.

Citation Envoyé par SylvainPV  Voir le message
La programmation synchrone est appréciée car elle se lit comme un texte, de haut en bas. Mais les programmes n'ont pas une chronologie fixe et un scénario à sens unique. En particulier pour la programmation orientée objet ou orientée évènement: c'est prévu pour partir dans tous les sens ! Et je ne comprends pas l'intérêt de s'efforcer à lutter contre ça, c'est inexorable. Je ne crois pas qu'il faille adapter le langage, mais plutôt adapter notre manière de le lire. Plutôt que de parcourir un code en le défilant à la molette de la souris, on a qu'à utiliser davantage les raccourcis de navigation de certains IDE comme "Aller à la déclaration de cette fonction" ou "Trouver les références de cette fonction".

Personnellement, je trouve qu'un code qui part dans tous les sens n'est pas forcément aisé à maintenir, surtout quand on ne l'a pas développé, soi-même.

De plus, toujours à mon sens, je pense que développer en fonction des fonctionnalités de l'IDE est une mauvaise pratique.

Les facilités offertes par un IDE devraient le rester, sans plus, plutôt que de se dire, "puisque mon EDI me facilite la relecture, pourquoi organiser mon code?" (en résumé, hein).

D'autant plus que certains, comme moi, font encore tout avec Notepad++ ou d'autres éditeurs moins évolués.
Avatar de Lcf.vs Lcf.vs - Membre éprouvé https://www.developpez.com
le 09/08/2013 à 1:39
Voici ce que donnerait ton code, avec yld :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 
function onFormSubmit(submitEvent) { 
    var yldSendXHR, response, delay, notification; 
     
    yldSendXHR = yield this.yld(sendXHR)({ 
        uri: "http://example.com/upload", 
        body: document.querySelector('input').value, 
        method: "POST" 
    }); 
     
    // jusqu'à ce point, ton code n'est exécuté qu'une seule fois, par submit 
     
    while (response === undefined || response.status !== 200) { 
        [delay, notification] = response === undefined ? [0, "Sending data..."] : [5000, "Server down, trying again in 5 seconds"]; 
         
        showNotification(notification); 
         
        response = yield setTimeout(yldSendXHR.send, delay); 
    } 
 
    yield showNotification("Upload done : " + response.responseText); 
} 
  
function sendXHR(request) { 
    var parent, xhr; 
     
    parent = this.parent; 
 
    xhr = new XMLHttpRequest(); 
    xhr.open(request.method, request.uri, true); 
    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 
 
    xhr.onreadystatechange = function () { 
        if (this.readyState === 4) { 
            parent.send(this); 
        } 
    }; 
  
    yield parent.send(this); 
 
    // jusqu'à ce point, ton code n'est exécuté qu'une seule fois, par submit 
     
    while (true) { 
        yield xhr.send(request.body); 
    } 
} 
  
var form = document.querySelector('form'); 
form.onsubmit = yld(onFormSubmit);
Tu remarqueras que je n'ai qu'une seule fonction servant au traitement, l'autre est utilitaire (AJAX).
Avatar de SylvainPV SylvainPV - Rédacteur/Modérateur https://www.developpez.com
le 09/08/2013 à 11:04
Citation Envoyé par Lcf.vs  Voir le message
Personnellement, je trouve qu'un code qui part dans tous les sens n'est pas forcément aisé à maintenir, surtout quand on ne l'a pas développé, soi-même.

De plus, toujours à mon sens, je pense que développer en fonction des fonctionnalités de l'IDE est une mauvaise pratique.

Les facilités offertes par un IDE devraient le rester, sans plus, plutôt que de se dire, "puisque mon EDI me facilite la relecture, pourquoi organiser mon code?" (en résumé, hein).

D'autant plus que certains, comme moi, font encore tout avec Notepad++ ou d'autres éditeurs moins évolués.

C'est précisément sur ce point que l'on est pas d'accord. Pour moi, un code verticalisé n'est pas pour autant mieux organisé. On en revient au débat classique des paradigmes de programmation.

De même que certains critiquent la programmation orientée objet pour le cloisonnement et le parallélisme apporté aux objets que l'on manipule, d'autres affirment qu'on y gagne en lisibilité en apportant davantage de sens et d'associations à des objets réels de la vie courante. Et preuve en est aujourd'hui que la POO a su s'imposer.

Le JavaScript a cette richesse qui lui permet de coder en suivant ces différentes approches. Quand j'ai commencé à coder en JS, j'utilisais une programmation impérative et séquentielle, très verticale (d'ailleurs je n'avais souvent qu'un seul fichier JS). Puis plus tard avec l'expérience j'ai davantage modularisé mon code et commencé à manipuler les prototypes et les constructeurs. Enfin depuis que je travaille avec Node.js, j'adopte de plus en plus une programmation évènementielle. Je trouve que ce paradigme se prête particulièrement bien à la communication client-serveur et aux applications dites temps réel.

De la même façon que j'ai évolué dans mon style de programmation, mes outils ont également évolué. J'ai moi aussi commencé avec Notepad++, mais aujourd'hui je ne lâcherais pour rien au monde mon IDE front (WebStorm). Je pense que la perte en productivité serait catastrophique. L'outillage est essentiel pour un programmeur et tu ne devrais pas négliger cet aspect.
Avatar de Lcf.vs Lcf.vs - Membre éprouvé https://www.developpez.com
le 09/08/2013 à 11:48
Moui, je suis entièrement d'accord avec toi, c'est une question de préférences, autant concernant la façon de coder que concernant les outils.

Sinon, l'exemple AJAX te va?
Avatar de SylvainPV SylvainPV - Rédacteur/Modérateur https://www.developpez.com
le 09/08/2013 à 14:28
Je t'avoue que j'ai beaucoup de mal avec cette ligne :
Code : Sélectionner tout
yield this.yld(sendXHR)(params)
Pas simple à comprendre quand le prototype est Function function(Function f), et qu'en plus on l'exécute directement et on passe le résultat à l'opérateur yield. De ce que j'ai compris, c'est une sorte de wrapper qui va gérer à notre place les yield "en cascade" à l'intérieur de la fonction passée, c'est bien ça ?

Et puis il y a la boucle while (true) qui me fait tiquer. Pas de break, pas de return, pas d'exception, j'ai bien du mal à comprendre quand est-ce qu'on sort de cette boucle, et pourquoi avoir mis une telle condition.
Avatar de Lcf.vs Lcf.vs - Membre éprouvé https://www.developpez.com
le 09/08/2013 à 19:50
Citation Envoyé par SylvainPV  Voir le message
De ce que j'ai compris, c'est une sorte de wrapper qui va gérer à notre place les yield "en cascade" à l'intérieur de la fonction passée, c'est bien ça ?

C'est tout à fait cela.

Citation Envoyé par SylvainPV  Voir le message
Et puis il y a la boucle while (true) qui me fait tiquer. Pas de break, pas de return, pas d'exception, j'ai bien du mal à comprendre quand est-ce qu'on sort de cette boucle, et pourquoi avoir mis une telle condition.

On ne peut pas mettre de return dans une fonction contenant au moins un yield, dans son scope.

Concernant les boucles, c'est assez simple...

La boucle du onFormSubmit :

Étant donné que l'on va envoyer des requêtes AJAX et qu'on ne sait pas combien d'échecs on va avoir, avant le success, on ne peut savoir combien de yield on va avoir besoin.

On a donc une boucle qui va nous "retourner" autant de yield qu'on en a besoin, appelant le générateur de sendXHR.

Comme tu as pu le voir, la condition de cette boucle est basée sur le status de la réponse du générateur de sendXHR, cette boucle n'est donc pas infinie, si à un moment donné, on réussit notre requête.

La boucle du sendXhr :

Comme la boucle précédente, ne sachant pas combien de fois le processus parent va appeler processus enfant, on ne sait pas combien on aura besoin de yield.

Cette boucle n'a pas de condition de sortie, elle produira donc autant de yield qu'il y aura d'appels de la méthode next() du générateur retourné par sendXHR.

Cette boucle s'arrêtera donc dès qu'on n'y fait plus appel.
Avatar de yahiko yahiko - Rédacteur/Modérateur https://www.developpez.com
le 09/08/2013 à 20:40
C'est intéressant ce yield.

Ça peut offrir une solution d'implémentation supplémentaire pour les appels asynchrones.

J'avais étudié un peu les promises, et je n'avais pas été super emballé par le côté un peu abstrait du concept.

Là où je rejoins SylvainPV, c'est que les callbacks ont leurs défauts mais ne nécessitent pas un grand effort intellectuel pour comprendre la mécanique. On reste dans un style de programmation classique et donc éprouvé.

Néanmoins, je pense que ce yield mérite qu'on s'y attarde un peu. Faudra que je prenne le temps de tester mais l'idée que le traitement ne poursuive pas au delà du yield peut avoir son intérêt.

Merci pour la veille techno ;-)
Offres d'emploi IT
Spécialiste systèmes informatiques qualité et référent procédure H/F
Safran - Ile de France - Colombes (92700)
Ingénieur H/F
Safran - Ile de France - Moissy-Cramayel (77550)
Architecte technique des systèmes d'information H/F
Safran - Ile de France - Évry (91090)

Voir plus d'offres Voir la carte des offres IT
Responsable bénévole de la rubrique JavaScript : Xavier Lecomte -