2010-01-29 24 views
13

Estoy leyendo this article y la sección sobre la abstracción de la promesa me parece un poco demasiado complicada. La siguiente se da como un ejemplo:¿Cuál es el beneficio de una abstracción 'promesa' en 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); 
    }); 

Me parece que la siguiente podría proporcionar el mismo resultado con menos líneas de código:

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

Usted tienen razón, no tiene sentido usar promesas para operaciones sincrónicas. Entonces el resultado debe ser igual. Pero luego es un ejemplo e ilustra el uso de la promesa. Para el código que se ejecuta después de su ejemplo, de hecho hay una diferencia. Si necesita ejecutar algo después del ejemplo de lo que puede hacerlo (utilizando el enfoque de promesa) sin saber nada sobre lo que hace el código de ejemplo –

Respuesta

17

Si bien es cierto que tanto en última instancia, lograr la Lo mismo, la diferencia es que su segundo ejemplo no es asincrónico. Por ejemplo, considere lo que sucede si JSON.parse(...) resulta ser una operación extremadamente costosa; Tendrás que esperar hasta que todo haya terminado, lo que puede no ser siempre lo que deseas.

Eso es lo que le ofrece la promesa: la poderosa capacidad de diferir el cálculo de la respuesta correcta hasta un momento más conveniente. Como su nombre indica, la construcción "promete" darle el resultado en algún momento, pero no necesariamente ahora. Puede leer más sobre futuros y promesas de trabajo en una escala más grande here.

+2

¿Qué hora es conveniente aquí? Si la operación es extremadamente costosa y JSON.parse es una pieza de código JavaScript, se bloqueará de todos modos. La diferencia es solo eso con la promesa de que puedes terminar las funciones que realmente funcionan. –

+3

Es cierto que el análisis tardará la misma cantidad de tiempo, independientemente de si se calcula de forma síncrona o asíncrona. Sin embargo, si se toma el tiempo de implementar su analizador sintáctico de manera que ceda predeciblemente al bucle de evento después de completar pequeños fragmentos de la operación, se puede ejecutar otro código asíncrono de forma asincrónica entre cada fragmento. Esto hace que una aplicación sea más receptiva, no más rápida. –

+0

O JSON.parse podría ser un método nativo y ejecutar en otro hilo – Jan

3

Vamos a comparar el ejemplo promesa de un ejemplo de JavaScript puro:

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

Como Norbert señaló Hartl, JSON.parse() colgará el navegador para grandes cadenas. Así que utilicé setTimeout() para retrasar su ejecución (después de una pausa de 10 milisegundos). Este es un ejemplo de la solución de Kris Kowal. Permite completar el hilo de Javascript actual, liberando el navegador para presentar los cambios DOM y desplazarse por la página para el usuario, antes de ejecutar la devolución de llamada.

Espero que el framework de promesa commonjs también use algo como setTimeout, de lo contrario, las últimas promesas en el ejemplo del artículo se ejecutarán sincrónicamente como se temía.

Mi alternativa anterior parece bastante fea, con los procesos posteriores que requieren más sangría. Reestructuré el código, por lo que podemos ofrecer nuestra cadena de procesos todo en una planta:

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

Tenía la esperanza de demostrar que tradicional visión de paso de las devoluciones de llamada en Javascript es más o menos equivalente a las promesas. Sin embargo, después de dos intentos, parezco haber demostrado, con referencia a la pulcritud del código en el ejemplo original, ¡que las promesas son una solución mucho más elegante!

0

También se podría agregar que la ventaja de la primera versión sobre la segunda es que separa las diferentes operaciones en la cadena de refinamiento (las funciones tampoco tienen que escribirse en el lugar). La segunda versión mezcla el análisis de bajo nivel con la lógica de la aplicación. Específicamente, utilizando los principios SOLID como directrices, la segunda versión infringe OCP y SRP.

1

El segundo fragmento es vulnerable al ataque de denegación de servicio porque example.com/foo puede devolver el json no válido para bloquear el servidor. Incluso la respuesta vacía es JSON no válida (aunque es válida JS). Es como mysql_* ejemplos con agujeros de inyección de SQL evidentes.

Y el código de promesa también puede mejorarse.Estos son iguales:

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

Y:

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 quisiéramos manejar el error, entonces éstas serían iguales:

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

y:

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