2010-01-29 9 views
13

Sto leggendo this article e la sezione sull'astrazione promessa mi sembra un po 'troppo complicata. Di seguito è dato a titolo di esempio:Qual è il vantaggio di una "promessa" di astrazione 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); 
    }); 

Mi sembra che il seguente potrebbe fornire lo stesso risultato con un minor numero di righe di codice:

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

È hanno ragione, non ha senso usare le promesse per le operazioni sincrone. Quindi il risultato dovrebbe essere uguale. Ma poi è un esempio e illustra l'uso della promessa. Per il codice che è in esecuzione dopo il tuo esempio, c'è davvero una differenza. Se è necessario eseguire qualcosa dopo l'esempio di quello che è possibile fare (utilizzando l'approccio promettente) senza sapere nulla su ciò che sta facendo il codice di esempio –

risposta

17

Se è vero che entrambi saranno in ultima analisi, realizzare il stessa cosa, la differenza è che il tuo secondo esempio non è asincrono. Ad esempio, considerare cosa succede se JSON.parse(...) risulta essere un'operazione estremamente costosa; dovrai rimanere sospeso fino a quando tutto è finito, il che potrebbe non essere sempre quello che desideri.

Questo è ciò che promette si ottiene: la potente capacità di differire il calcolo della risposta giusta a un momento più conveniente. Come suggerisce il nome, il costrutto "promette" di darti il ​​risultato ad un certo punto, ma non necessariamente in questo momento. Puoi leggere di più su futures e promesse di lavoro su scala più ampia here.

+2

Che cosa è un tempo conveniente qui? Se l'operazione è estremamente costosa e JSON.parse è un pezzo di codice javascript, si bloccherà comunque. La differenza è solo quella con la promessa che puoi completare le funzioni effettivamente in esecuzione. –

+3

È vero che l'analisi richiederà la stessa quantità di tempo indipendentemente dal fatto che sia calcolato in modo sincrono o asincrono. Tuttavia, se si impiega il tempo necessario per implementare il parser in modo tale che produca prevedibilmente il ciclo degli eventi dopo aver completato piccoli frammenti dell'operazione, un altro codice asincrono può essere eseguito in modo asincrono tra ciascun blocco. Ciò rende un'applicazione più reattiva, non più veloce. –

+0

Oppure JSON.parse potrebbe essere un metodo nativo ed essere eseguito su un'altra discussione – Jan

3

Confrontiamo l'esempio promessa di un puro esempio 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); 
}); 

Come Norbert Hartl ha sottolineato, JSON.parse() si bloccherà il browser per stringhe di grandi dimensioni. Quindi ho usato setTimeout() per ritardarne l'esecuzione (dopo una pausa di 10 millisecondi). Questo è un esempio della soluzione di Kris Kowal. Consente il completamento del thread Javascript corrente, liberando il browser per presentare le modifiche al DOM e scorrere la pagina per l'utente, prima dell'esecuzione del callback.

spero che il quadro promessa commonjs inoltre usa qualcosa come setTimeout, altrimenti le promesse più tardi nel l'esempio del articolo sarà davvero funzionare in modo sincrono come temuto.

La mia alternativa sopra sembra piuttosto brutto, con i processi successivi che richiedono un ulteriore rientro. Ho ristrutturato il codice, in modo che possiamo fornire la nostra catena di processo tutto in un unico livello:

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

speravo di dimostrare che tradizionale previsionali passaggio di callback in JavaScript è più o meno equivalente a promesse. Tuttavia, dopo due tentativi, sembra che abbia mostrato, con riferimento alla nitidezza del codice nell'esempio originale, che le promesse sono una soluzione molto più elegante!

0

Si può anche aggiungere che il vantaggio della prima versione sopra la seconda è che separa diverse operazioni della catena di affinamento (le funzioni non devono essere scritti sul posto o). La seconda versione mescola sia l'analisi di basso livello con la logica dell'applicazione. In particolare, utilizzando i principi SOLID come linee guida, la seconda versione viola sia OCP e SRP.

1

Il secondo frammento è vulnerabile ad attacco denial of service perché example.com/foo può semplicemente restituire JSON valido per far crashare il server. Anche la risposta vuota è JSON non valida (sebbene JS valido). È come esempi mysql_* con buchi di iniezione SQL abbagliante.

E anche il codice di promessa può essere migliorato.Questi sono uguali:

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

E:

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

Se volessimo gestire l'errore, allora questi sarebbero uguali:

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

e:

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