9.3 KiB
Observables en comparación con otras técnicas
A menudo puedes usar observables en lugar de promesas para entregar valores de forma asíncrona. Del mismo modo, los observables pueden reemplazar a los controladores de eventos. Finalmente, porque los observables entregan múltiples valores, puedes usarlos donde de otro modo podrías construir y operar con arrays.
Los observables se comportan de manera algo diferente a las técnicas alternativas en cada una de estas situaciones, pero ofrecen algunas ventajas significativas. Aquí hay comparaciones detalladas de las diferencias.
Observables en comparación con promesas
Los observables a menudo se comparan con las promesas. Aquí hay algunas diferencias clave:
-
Los observables son declarativos; La ejecución no comienza hasta la suscripción. Las promesas se ejecutan inmediatamente después de la creación. Esto hace que los observables sean útiles para definir recetas que se pueden ejecutar cuando necesites el resultado.
-
Los observables proporcionan muchos valores. Las promesas proporcionan un valor. Esto hace que los observables sean útiles para obtener múltiples valores a lo largo del tiempo.
-
Los observables diferencian entre encadenamiento y suscripción. Las promesas solo tienen cláusulas
.then ()
. Esto hace que los observables sean útiles para crear recetas de transformación complejas para ser utilizadas por otra parte del sistema, sin que el trabajo se ejecute. -
Observables
subscribe()
es responsable de manejar los errores. Las promesas empujan los errores a promesas hijas. Esto hace que los observables sean útiles para el manejo centralizado y predecible de errores.
Creación y suscripción
-
Los observables no se ejecutan hasta que un consumidor se suscribe. El
subscribe()
ejecuta el comportamiento definido una vez, y se puede volver a llamar. Cada suscripción tiene su propia computación. La resuscripción provoca la recomputación de los valores. -
Las promesas se ejecutan de inmediato, y solo una vez. La computación del resultado se inicia cuando se crea la promesa. No hay forma de reiniciar el trabajo. Todas las cláusulas
then
(suscripciones) comparten la misma computación.
Encadenamiento
-
Los observables diferencian entre la función de transformación, como
map
ysubscription
. Solo la suscripción activa la función de suscriptor para comenzar a calcular los valores. -
Las promesas no diferencian entre las últimas cláusulas
.then
(equivalentes al subscription) y las cláusulas intermedias.then
(equivalentes al map).
Cancelación
-
Las suscripciones de los observables son cancelables. La cancelación de la suscripción evita que el oyente reciba más valores y notifica a la función del suscriptor que cancele el trabajo.
-
Las promesas no son cancelables.
Manejo de errores
-
Los errores de ejecución en observables se entregan al controlador de errores del suscriptor, y el suscriptor cancela automáticamente la suscripción del observable.
-
Las promesas empujan los errores a las promesas hijas.
Hoja de trucos
Los siguientes fragmentos de código ilustran cómo se define el mismo tipo de operación utilizando observables y promesas.
Operation | Observable | Promise |
---|---|---|
Creation |
new Observable((observer) => { observer.next(123); }); |
new Promise((resolve, reject) => { resolve(123); }); |
Transform | obs.pipe(map((value) => value * 2)); |
promise.then((value) => value * 2); |
Subscribe |
sub = obs.subscribe((value) => { console.log(value) }); |
promise.then((value) => { console.log(value); }); |
Unsubscribe | sub.unsubscribe(); |
Implied by promise resolution. |
Observables en comparación con eventos API
Los observables son muy similares a los controladores de eventos que usan la API de eventos. Ambas técnicas definen manejadores de notificaciones y las utilizan para procesar múltiples valores entregados a lo largo del tiempo. Suscribirse a un observable es equivalente a agregar un detector de eventos. Una diferencia significativa es que puedes configurar un observable para transformar un evento antes de pasar el evento al controlador.
El uso de observables para manejar eventos y operaciones asíncronas puede tener la ventaja de una mayor coherencia en contextos como las solicitudes HTTP.
Aquí hay algunos ejemplos de código que ilustran cómo se define el mismo tipo de operación usando observables y la API de eventos.
Observable | Events API | |
---|---|---|
Creation & cancellation |
// Setup const clicks$ = fromEvent(buttonEl, ‘click’); // Begin listening const subscription = clicks$ .subscribe(e => console.log(‘Clicked’, e)) // Stop listening subscription.unsubscribe(); |
function handler(e) { console.log(‘Clicked’, e); } // Setup & begin listening button.addEventListener(‘click’, handler); // Stop listening button.removeEventListener(‘click’, handler); |
Subscription |
observable.subscribe(() => { // notification handlers here }); |
element.addEventListener(eventName, (event) => { // notification handler here }); |
Configuration | Listen for keystrokes, but provide a stream representing the value in the input.
fromEvent(inputEl, 'keydown').pipe( map(e => e.target.value) ); |
Does not support configuration.
element.addEventListener(eventName, (event) => { // Cannot change the passed Event into another // value before it gets to the handler }); |
Observables en comparación con arrays
Un observable produce valores a lo largo del tiempo. Se crea un array como un conjunto estático de valores. En cierto sentido, los observables son asíncronos mientras que los arrays son síncronos. En los siguientes ejemplos, ➞ implica entrega de valor asíncrono.
Observable | Array | |
---|---|---|
Given |
obs: ➞1➞2➞3➞5➞7 obsB: ➞'a'➞'b'➞'c' |
arr: [1, 2, 3, 5, 7] arrB: ['a', 'b', 'c'] |
concat() |
concat(obs, obsB) ➞1➞2➞3➞5➞7➞'a'➞'b'➞'c' |
arr.concat(arrB) [1,2,3,5,7,'a','b','c'] |
filter() |
obs.pipe(filter((v) => v>3)) ➞5➞7 |
arr.filter((v) => v>3) [5, 7] |
find() |
obs.pipe(find((v) => v>3)) ➞5 |
arr.find((v) => v>3) 5 |
findIndex() |
obs.pipe(findIndex((v) => v>3)) ➞3 |
arr.findIndex((v) => v>3) 3 |
forEach() |
obs.pipe(tap((v) => { console.log(v); })) 1 2 3 5 7 |
arr.forEach((v) => { console.log(v); }) 1 2 3 5 7 |
map() |
obs.pipe(map((v) => -v)) ➞-1➞-2➞-3➞-5➞-7 |
arr.map((v) => -v) [-1, -2, -3, -5, -7] |
reduce() |
obs.pipe(reduce((s,v)=> s+v, 0)) ➞18 |
arr.reduce((s,v) => s+v, 0) 18 |