IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

[Article] Découvrez le yielded style programming ou développement JavaScript asynchrone linéaire


Sujet :

JavaScript

  1. #1
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut [Article] Découvrez le yielded style programming ou développement JavaScript asynchrone linéaire
    Découvrez le développement JavaScript asynchrone linéaire, avec yld

    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    <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?
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  2. #2
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    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...
    One Web to rule them all

  3. #3
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut Comparaison des différentes méthodes
    @ SylvainPV :

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

    Le callback :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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.
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  4. #4
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    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 - Visualiser dans une fenêtre à part
    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 - Visualiser dans une fenêtre à part
    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".
    One Web to rule them all

  5. #5
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    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.
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  6. #6
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    Voici ce que donnerait ton code, avec yld :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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).
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  7. #7
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    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.
    One Web to rule them all

  8. #8
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    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?
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  9. #9
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Je t'avoue que j'ai beaucoup de mal avec cette ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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.
    One Web to rule them all

  10. #10
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    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.
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  11. #11
    Rédacteur/Modérateur

    Avatar de yahiko
    Homme Profil pro
    Développeur
    Inscrit en
    Juillet 2013
    Messages
    1 423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 1 423
    Points : 8 699
    Points
    8 699
    Billets dans le blog
    43
    Par défaut
    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 ;-)
    Tutoriels et FAQ TypeScript

  12. #12
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    @yahiko : Merci pour ton intérêt.

    Processus détaillé

    Prenons une fonction retournant un générateur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    var fct;
     
    fct = function fct(value) {
        yield value + 1;
        yield value + 2;
    };
    Passons-la à yld :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    var yielded;
     
    yielded = yld(fct);
    Envoyons une valeur :
    À l'exécution de yielded, mon outil récupère le générateur et envoie un premier generator.next(), il s'arrête donc au premier yield.

    Ensuite, cela dépend de ce qui est défini dans le générateur.

    Puisqu'il n'y a pas de this.send() dedans, on ne saute pas au yield suivant, le yield value + 2 ne sera donc jamais atteint.


    Modifions donc notre fonction de manière à atteindre le 2ème yield :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    var fct;
     
    fct = function fct(value) {
        value = value + 1;
        yield this.send();
        // console.log('2ème yield bientôt atteint');
        yield value + 2;
    };
    Exemple avec une réponse :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    var fct;
     
    fct = function fct(value) {
        var response;
     
        response = yield this.send(value + 1);
        yield response + 2;
    };
    Exemple envoyant la valeur actuelle, toutes les 2 secondes (sans fin) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    var fct;
     
    fct = function fct(value) {
        var self, response;
     
        self = this;
        response = value;
     
        while (true) {
            response = yield setTimeout(function () {
                self.send(response + 1);
            }, 2000);
        }
    };
    Exemple identique au précédent, tant que response !== 5 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    var fct;
     
    fct = function fct(value) {
        var self, response;
     
        self = this;
        response = value;
     
        while (response !== 5) { // si value est égal à 1, on ne bouclera que 4 fois
            response = yield setTimeout(function () {
                self.send(response + 1);
            }, 2000);
        }
    };
    Exemple avec un processus enfant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    var fct, fctBis;
     
    fct = function fct(value) {
        var response, child;
     
        response = value;
     
        child = yield this.yld(fctBis)();
     
        while (response !== 5) { // si value est égal à 1, on ne bouclera que 4 fois
            response = yield setTimeout(function () {
                child.send(response);
            }, 2000);
        }
    };
     
    fctBis = function fctBis() {
        var parent, response;
     
        parent = this.parent;
     
        response = yield parent.send(this);
     
        while (true) {
            response = yield parent.send(response + 1);
        }
    };
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  13. #13
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    Un autre avantage important, par rapport aux callbacks et promises, c'est le problème de conservation de variables déjà calculées, dès que l'on veut externaliser ses fonctions...

    Voici un exemple avec le callback, il est tout aussi valable avec les promises.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    var call, asyncFct;
     
    call = function call() {
        var callValue;
     
        callValue = 1;
     
        asyncFct(function (error, value) {
             console.log(callValue + value);
        });
    };
     
    asyncFct = function asyncFct(callback) {
        callback(null, 1);
    };
     
    call();
    À l'inverse, puisque dans une yielded function, on récupère la réponse dans le scope courant, il est donc inutile de créer une nouvelle fonction à chaque appel.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    var call, asyncFct;
     
    call = function call() {
        var callValue, response;
     
        callValue = 1;
     
        response = yield this.yld(asyncFct)();
     
        yield console.log(callValue + response);
    };
     
    asyncFct = function asyncFct() {
        yield this.parent.send(1);
    };
     
    yld(call)();
    Cela peut vous permettre, par exemple, d'éviter de devoir freezer des objets dont vous avez besoin dans le traitement de la réponse mais que vous ne voudriez pas voir modifié ou de devoir définir des getters/setters, afin de restreindre les conditions d'accès/modification.

    Gardez vos propriétés privées, vraiment privées.
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  14. #14
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    NPM Node.js enregistré : https://npmjs.org/package/yld
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  15. #15
    Rédacteur/Modérateur

    Avatar de yahiko
    Homme Profil pro
    Développeur
    Inscrit en
    Juillet 2013
    Messages
    1 423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 1 423
    Points : 8 699
    Points
    8 699
    Billets dans le blog
    43
    Par défaut
    Prolifique dis-donc.

    Tu es devenu un véritable évangéliste du yield !
    Tutoriels et FAQ TypeScript

  16. #16
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    Citation Envoyé par yahiko Voir le message
    Prolifique dis-donc.

    Tu es devenu un véritable évangéliste du yield !
    Ben, j'essaie d'apporter ce que je peux...
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  17. #17
    Membre du Club
    Inscrit en
    Janvier 2008
    Messages
    64
    Détails du profil
    Informations forums :
    Inscription : Janvier 2008
    Messages : 64
    Points : 53
    Points
    53
    Par défaut
    J'ai rien compris à quoi qu'il servait ce machin XD ... j'm'en va étudier ca ^^

  18. #18
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    Citation Envoyé par faucheuse Voir le message
    J'ai rien compris à quoi qu'il servait ce machin XD ... j'm'en va étudier ca ^^
    Si après avoir tout lu/testé, il y a encore des points d'ombre, n'hésite pas à me poser tes questions.
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  19. #19
    Membre expert
    Avatar de ThomasR
    Homme Profil pro
    Directeur technique
    Inscrit en
    Décembre 2007
    Messages
    2 230
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Décembre 2007
    Messages : 2 230
    Points : 3 972
    Points
    3 972
    Par défaut
    Salut Lcf,

    J'ai un peu suivi alors je me permet de commenter.
    Personnellement je trouve que c'est une POC intéressante, mais qu'elle ne répond à aucun besoin existant. De plus le mot-clé yield permet de créer des générateurs (ou itérateurs) et là on s'en écarte sacrément. Je suis jamais fan des transgressions fonctionnelles comme ceci.

    Je me posais une question aussi, dans ta doc tu indiques qu'on peut récupérer la réponse depuis un autre scope comme ceci, avec simplement l'appel à yield :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        response = yield; // response is 123
    Est-ce que ca signifie qu'on ne peut pas deux appels simultanés à yld ?

    En effet, imagine qu'on a deux appels AJAX simultanés qui vont tous les deux s'appuyer sur ta fonction yld dans leur callback de réponse, que se passe-t-il dans ce cas à l'appel de "yield" dans ces deux fonctions ?

    Mais bon j'ai surement raté un truc

    Merci

  20. #20
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    741
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 741
    Points : 808
    Points
    808
    Par défaut
    Salut Thomas,

    Tu soulèves, là, une question intéressante et je t'en remercie, bien qu'un réel besoin de les envoyer simultanément doit être extrêmement rare.

    Tout dépend de ce que tu entends par simultanées...

    Si tu veux simplement les envoyer depuis le même scope, l'une après l'autre, tu peux faire ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    onFormSubmit = function onFormSubmit(submitEvent) {
        var yldSendXHR, sender1, sender2, response1, response2, delay, notification;
     
        yldSendXHR = this.yld(sendXHR);
     
        sender1 = yield yldSendXHR({
            uri: 'http://example.com/upload1',
            body: document.querySelector('input').value,
            method: 'POST'
        });
     
        sender2 = yield yldSendXHR({
            uri: 'http://example.com/upload2',
            body: document.querySelector('input').value,
            method: 'POST'
        });
     
        while (response1 === undefined || response1.status !== 200) {
            [delay, notification] = response1 === undefined ? [0, 'Sending data...'] : [5000, 'Server down, trying again in 5 seconds'];
     
            console.log(notification);
     
            response1 = yield setTimeout(sender1.send, delay);
        }
     
        console.log('Upload done : ' + response1.responseText);
     
        while (response2 === undefined || response2.status !== 200) {
            [delay, notification] = response2 === undefined ? [0, 'Sending data...'] : [5000, 'Server down, trying again in 5 seconds'];
     
            console.log(notification);
     
            response2 = yield setTimeout(sender2.send, delay);
        }
     
        yield console.log('Upload done : ' + response2.responseText);
    };
    Dans le cas où tes appels doivent vraiment être envoyés en même temps, plusieurs solutions sont possibles...

    Soit tu as 2 fonctions qui seront des contextes parents de sendXHR.

    Soit on modifie quelque peu sendXHR, de manière à pouvoir identifier la réponse de sendXHR.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
        xhr.onreadystatechange = function () {
            if (this.readyState === 4) {
                parent.send(Object.create(this, {
                    // permettra de savoir quelle est la requête qui répond
                    request: request
                }));
            }
        };
    Après, soit on compare l'objet request, lui-même, soit on y définit un id (par exemple), qu'il suffira de récupérer lorsqu'on obtient une réponse.

    Le onFormSubmit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    onFormSubmit = function onFormSubmit(submitEvent) {
        var yldSendXHR, sender1, sender2, response, text1, text2;
     
        yldSendXHR = this.yld(sendXHR);
     
        sender1 = yield yldSendXHR({
            id: 'sender1',
            uri: 'http://example.com/upload1',
            body: document.querySelector('input').value,
            method: 'POST'
        });
     
        sender2 = yield yldSendXHR({
            id: 'sender2',
            uri: 'http://example.com/upload2',
            body: document.querySelector('input').value,
            method: 'POST'
        });
     
        while (text1 === undefined || text2 === undefined) {
            if (response === undefined) {
                setTimeout(sender1.send, 0);
                setTimeout(sender2.send, 0);
            } else {
                if (response.status !== 200) {
                    setTimeout((response.id === 'sender1' ? sender1 : sender2).send, 5000);
                } else {
                    if (response.id === 'sender1') {
                        text1 = response.responseText;
                    } else {
                        text2 = response.responseText;
                    }
                }
            }
     
            if (text1 === undefined || text2 === undefined) {
                response = yield;
            }
        }
     
        console.log('Multiple upload done');
        console.log('Upload 1 response : ' + text1);
        console.log('Upload 2 response : ' + text2);
        yield;
    };
    En revanche, je pensais que le fait que chaque contexte garde ses variables chez lui, ainsi que la relation entre les différents scopes, te sembleraient intéressants...
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

Discussions similaires

  1. Réponses: 0
    Dernier message: 21/03/2012, 17h09
  2. Réponses: 1
    Dernier message: 07/10/2010, 23h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo