Al hacer una programación asincrónica de un solo subproceso, hay dos técnicas principales con las que estoy familiarizado. El más común es usar devoluciones de llamada. Eso significa pasar a la función que actúa asincrónicamente una función de devolución de llamada como parámetro. Cuando finalice la operación asincrónica, se llamará a la devolución de llamada.
Algún jQuery
código típico diseñado de esta manera:
$.get('userDetails', {'name': 'joe'}, function(data) {
$('#userAge').text(data.age);
});
Sin embargo, este tipo de código puede volverse desordenado y altamente anidado cuando deseamos realizar llamadas asíncronas adicionales una tras otra cuando finalice la anterior.
Entonces, un segundo enfoque es usar Promesas. Una promesa es un objeto que representa un valor que aún podría no existir. Puede establecer devoluciones de llamada, que se invocarán cuando el valor esté listo para leerse.
La diferencia entre las promesas y el enfoque tradicional de devoluciones de llamada es que los métodos asíncronos ahora devuelven sincrónicamente los objetos de promesa, en los que el cliente establece una devolución de llamada. Por ejemplo, código similar usando Promises en AngularJS:
$http.get('userDetails', {'name': 'joe'})
.then(function(response) {
$('#userAge').text(response.age);
});
Entonces mi pregunta es: ¿hay realmente una diferencia real? La diferencia parece ser puramente sintáctica.
¿Hay alguna razón más profunda para usar una técnica sobre la otra?
Respuestas:
Es justo decir que las promesas son solo azúcar sintáctica. Todo lo que puede hacer con promesas lo puede hacer con devoluciones de llamada. De hecho, la mayoría de las implementaciones prometedoras proporcionan formas de convertir entre los dos cuando lo desee.
La razón profunda por la cual las promesas son a menudo mejores es que son más componibles , lo que significa que combinar múltiples promesas "simplemente funciona", mientras que combinar múltiples devoluciones de llamadas a menudo no lo hace. Por ejemplo, es trivial asignar una promesa a una variable y adjuntar controladores adicionales más adelante, o incluso adjuntar un controlador a un gran grupo de promesas que se ejecutan solo después de que se resuelven todas las promesas. Si bien puede emular estas cosas con devoluciones de llamada, se necesita mucho más código, es muy difícil de hacer correctamente y el resultado final generalmente es mucho menos fácil de mantener.
Una de las formas más grandes (y sutiles) de que las promesas ganen su componibilidad es mediante el manejo uniforme de los valores de retorno y las excepciones no detectadas. Con las devoluciones de llamada, la forma en que se maneja una excepción puede depender completamente de cuál de las muchas devoluciones de llamada anidadas la arrojó, y cuál de las funciones que toman devoluciones de llamada tiene un intento / captura en su implementación. Con las promesas, sabe que una excepción que escapa a una función de devolución de llamada se detectará y pasará al controlador de errores que proporcionó con
.error()
o.catch()
.Para el ejemplo que usted dio de una única devolución de llamada versus una sola promesa, es cierto que no hay una diferencia significativa. Es cuando tienes un billón de devoluciones de llamada versus un billón de promesas que el código basado en promesas tiende a verse mucho mejor.
Aquí hay un intento de un código hipotético escrito con promesas y luego con devoluciones de llamada que deberían ser lo suficientemente complejas como para darle una idea de lo que estoy hablando.
Con promesas:
Con devoluciones de llamada:
Puede haber algunas formas inteligentes de reducir la duplicación de código en la versión de devolución de llamada incluso sin promesas, pero todas las que se me ocurren se reducen a implementar algo muy prometedor.
fuente
yield
promesas de educación. La ventaja aquí es que puede mezclar estructuras de flujo de control nativas, que pueden variar en la cantidad de operaciones asíncronas que realizan. Agregaré una versión que muestra esto.then(callback)
método en Promise que acepte una devolución de llamada (en lugar de un método en API que acepte esta devolución de llamada) no tiene que hacer nada con IoC. Promise introduce un nivel de indirección que es útil para la composición, el encadenamiento y el manejo de errores (programación orientada a ferrocarriles en efecto), pero el cliente aún no ejecuta la devolución de llamada, por lo que en realidad no hay ausencia de IoC.