2010-01-29 7 views
13

Ich lese this article und der Abschnitt über die Verheißung Abstraktion scheint ein wenig übermäßig kompliziert für mich. Das Folgende ist als ein Beispiel gegeben:Was ist der Vorteil einer "Versprechen" Abstraktion in CommonJS?

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); 
    }); 

Es scheint mir, dass die folgende könnte das gleiche Ergebnis mit weniger Codezeilen bieten:

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); 
    }); 
+2

Sie Es hat keinen Sinn, Versprechungen für synchrone Operationen zu verwenden. Also sollte das Ergebnis gleich sein. Aber dann ist es ein Beispiel und illustriert die Verwendung des Versprechens. Für den Code, der nach Ihrem Beispiel ausgeführt wird, gibt es tatsächlich einen Unterschied. Wenn Sie etwas nach dem Beispiel ausführen müssen, können Sie es tun (indem Sie den Versprechungsansatz verwenden), ohne etwas darüber zu wissen, was der Beispielcode macht –

Antwort

17

Es stimmt zwar, dass beide letztlich die voll Das Gleiche ist der Unterschied, dass Ihr zweites Beispiel nicht asynchron ist. Betrachten Sie zum Beispiel, was passiert, wenn sich JSON.parse(...) als ein extrem teurer Vorgang herausstellt; Sie müssen hängen, bis alles fertig ist, was nicht immer das ist, was Sie wollen.

Das ist, was Sie versprechen: die mächtige Fähigkeit, die Berechnung der richtigen Antwort auf eine bequemere Zeit zu verschieben. Wie der Name schon andeutet, "verspricht" das Konstrukt, Ihnen irgendwann das Ergebnis zu geben, gerade jetzt nicht unbedingt. Sie können mehr über Futures lesen und verspricht Arbeit in größerem Maßstab .

+2

Was ist eine günstige Zeit hier? Wenn die Operation extrem teuer ist und JSON.parse ein Stück JavaScript-Code ist, wird es sowieso hängen. Der Unterschied besteht nur darin, dass man mit dem Versprechen die tatsächlich laufenden Funktionen beenden kann. –

+3

Es stimmt, dass das Parsing die gleiche Zeit benötigt, unabhängig davon, ob es synchron oder asynchron berechnet wird. Wenn Sie sich jedoch die Zeit nehmen, Ihren Parser so zu implementieren, dass er nach der Fertigstellung kleiner Teile der Operation der Ereignisschleife vorhersehbar nachgibt, kann anderer asynchroner Code asynchron zwischen jedem Chunk ausgeführt werden. Dies macht eine Anwendung reaktionsfähiger, nicht schneller. –

+0

Oder JSON.parse könnte eine native Methode sein und auf einem anderen Thread – Jan

3

Lassen Sie uns das Versprechen Beispiel zu einem reinen Javascript Beispiel vergleichen:

// 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); 
}); 

Wie Norbert Hartl wies darauf hin, JSON.parse() wird der Browser für große Saiten hängen. Also habe ich setTimeout() verwendet, um die Ausführung zu verzögern (nach einer Pause von 10 Millisekunden). Dies ist ein Beispiel für Kris Kowals Lösung. Es erlaubt den aktuellen Javascript-Thread zu vervollständigen, den Browser freizugeben, um DOM-Änderungen anzuzeigen und die Seite für den Benutzer zu scrollen, bevor der Callback ausgeführt wird.

Ich hoffe, das commonjs-Versprechungs-Framework verwendet auch etwas wie setTimeout, sonst werden die späteren Versprechen im Beispiel des Artikels tatsächlich synchron wie befürchtet ablaufen.

Meine Alternative oben sieht ziemlich hässlich aus, mit den späteren Prozessen, die eine weitere Einrückung erfordern. Ich umstrukturiert den Code, so dass wir unsere Prozesskette alle in einer Ebene zur Verfügung stellen können:

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); 

Ich hoffte, dass traditionelle vorausGang Rückrufe in Javascript zu demonstrieren ziemlich gleichwertig Versprechen ist. Nach zwei Versuchen, die ich in Bezug auf die Ordentlichkeit des Codes im ursprünglichen Beispiel gezeigt habe, sind diese Versprechen jedoch eine weitaus elegantere Lösung!

0

Man könnte auch hinzufügen, dass der Vorteil der ersten Version gegenüber der zweiten Version darin besteht, dass sie verschiedene Operationen in der Verfeinerungskette trennt (die Funktionen müssen auch nicht an Ort und Stelle geschrieben sein). Die zweite Version mischt sowohl das Low-Level-Parsing mit der Anwendungslogik. Insbesondere unter Verwendung der SOLID-Prinzipien als Richtlinien verletzt die zweite Version sowohl OCP als auch SRP.

1

Das zweite Snippet ist anfällig für Denial-of-Service-Angriffe, da beispiel.com/foo nur ungültiges JSON zurückgeben kann, um den Server zum Absturz zu bringen. Auch leere Antwort ist ungültig JSON (obwohl gültig JS). Es ist wie mysql_* Beispiele mit grellen SQL-Injektionslöchern.

Und der Versprechen Code kann auch viel verbessert werden.Diese sind gleich:

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); 
    }); 

Und:

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); 
    }); 

Wenn wir den Fehler behandeln wollte, dann würden diese gleich:

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); 
    }); 

und:

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); 
    }); 
Verwandte Themen