Question

Je lis cet article et la section sur l'abstraction promesse me semble un peu trop compliqué à. Ce qui suit est donnée à titre d'exemple:

requestSomeData("http://example.com/foo") // returns a promise for the response
    .then(function(response){ // ‘then’ is used to provide a promise handler
        return JSON.parse(response.body); // parse the body
    }) // returns a promise for the parsed body
    .then(function(data){
        return data.price; // get the price
    }) // returns a promise for the price
    .then(function(price){ // print out the price when it is fulfilled
        print("The price is " + price);
    });

Il me semble que ce qui suit pourrait fournir le même résultat avec moins de lignes de code:

requestSomeData("http://example.com/foo")
    .requestHandler(function(response){
        // parse the body
        var data  = JSON.parse(response.body);

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });
Était-ce utile?

La solution

Il est vrai que les deux vont finalement faire la même chose, la différence est que votre deuxième exemple n'est pas asynchrone. Par exemple, pensez à ce qui se passe si JSON.parse(...) se révèle être une opération extrêmement coûteuse; vous devrez accrocher jusqu'à ce que tout est fini, ce qui ne peut pas toujours ce que vous voulez.

C'est ce que vous obtenez des promesses: la puissante capacité de reporter le calcul de la bonne réponse jusqu'à un moment plus opportun. Comme son nom l'indique, la construction « promesses » pour vous donner le résultat à un moment donné, mais pas nécessairement en ce moment. Vous pouvez en savoir plus sur l'avenir et promet de travailler à plus grande échelle .

Autres conseils

Comparons l'exemple de promesse à un pur exemple Javascript:

// First we need a convenience function for W3C's fiddly XMLHttpRequest.
// It works a little differently from the promise framework.  Instead of 
// returning a promise to which we can attach a handler later with .then(),
// the function accepts the handler function as an argument named 'callback'.

function requestSomeDataAndCall(url, callback) {
    var req = new XMLHttpRequest();
    req.onreadystatechange = resHandler;
    req.open("GET", url, false);
    req.send();
    function resHandler() {
        if (this.readyState==4 && this.status==200) {
            callback(this);
        } else {
            // todo: Handle error.
        }
    }
}

requestSomeDataAndCall("http://example.com/foo", function(res){
    setTimeout(function(){
        var data = JSON.parse(res.responseText);
        setTimeout(function(){
            var price = data.price;
            setTimeout(function(){
                print("The price is "+price);
            },10);
        },10);
    },10);
});

Comme Norbert Hartl a souligné, JSON.parse () se bloque le navigateur pour les grandes chaînes. Donc, je setTimeout () pour retarder son exécution (après une pause de 10 millisecondes). Ceci est un exemple de la solution de Kris Kowal. Il permet au fil Javascript courant pour terminer, libérant ainsi le navigateur pour présenter les changements DOM et faire défiler la page pour l'utilisateur, avant l'exécution de rappel.

J'espère que les CommonJS promettent cadre utilise aussi quelque chose comme setTimeout, sinon les promesses plus tard dans l'exemple de l'article courront en effet synchrone comme on le craignait.

Mon autre regarde au-dessus assez laid, avec les processus ultérieurs nécessitant plus en retrait. Je restructuré le code, afin que nous puissions fournir toute notre chaîne de processus dans un seul niveau:

function makeResolver(chain) {
    function climbChain(input) {
        var fn = chain.shift();      // This particular implementation
        setTimeout(function(){       // alters the chain array.
            var output = fn(input);
            if (chain.length>0) {
                climbChain(output);
            }
        },10);
    }
    return climbChain;
}

var processChain = [
    function(response){
        return JSON.parse(response.body);
    },
    function(data){
        return data.price; // get the price
    },
    function(price){
      print("The price is " + price);
    }
];

var climber = makeResolver(promiseChain);
requestSomeDataAndCall("http://example.com/foo", climber);

J'espérais démontrer que l'avant-passage traditionnel de callbacks en Javascript est à peu près équivalent à des promesses. Cependant, après deux tentatives, je semblent avoir montré, en se référant à l'aspect soigné du code dans l'exemple original, que les promesses sont une solution beaucoup plus élégante!

Le deuxième extrait est vulnérable à un déni de service attaque parce que example.com/foo peut juste retourner JSON invalide pour planter le serveur. Même réponse vide est JSON invalide (si JS valide). Il est comme des exemples de mysql_* avec des trous flagrants d'injection SQL.

Et le code de promesse peut être améliorée beaucoup aussi. Ceux-ci sont égaux:

requestSomeData("http://example.com/foo") // returns a promise for the response
    .then(function(response){ // ‘then’ is used to provide a promise handler
        // parse the body
        var data  = JSON.parse(response.body);

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });

requestSomeData("http://example.com/foo")
    .requestHandler(function(response){
        try {
            var data = JSON.parse(response.body);
        }
        catch(e) {
            return;
        }

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });

Si nous voulions gérer l'erreur, alors ceux-ci seraient égaux:

requestSomeData("http://example.com/foo") // returns a promise for the response
    .then(function(response){ // ‘then’ is used to provide a promise handler
        // parse the body
        var data  = JSON.parse(response.body);

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    }).catch(SyntaxError, function(e) {
        console.error(e);
    });

et

requestSomeData("http://example.com/foo")
    .requestHandler(function(response){
        try {
            var data = JSON.parse(response.body);
        }
        catch(e) {
            //If the above had a typo like `respons.body`
            //then without this check the ReferenceError would be swallowed
            //so this check is kept to have as close equality as possible with
            //the promise code
            if(e instanceof SyntaxError) {
                console.error(e);
                return;
            }
            else {
                throw e;
            }
        }

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });

On peut aussi ajouter que l'avantage de la première version sur le second est qu'il sépare les opérations différentes dans la chaîne de raffinage (les fonctions ne doivent pas être écrites soit en place). La deuxième version se mélange à la fois l'analyse syntaxique de bas niveau avec la logique de l'application. Plus précisément, en utilisant les principes solides comme des lignes directrices, la deuxième version viole à la fois OCP et SRP .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top