¿Cuál es la diferencia entre promesas y observables?
¿Cuál es la diferencia entre Promise
y Observable
en Angular?
Un ejemplo de cada uno sería útil para comprender ambos casos. ¿En qué escenario podemos utilizar cada caso?
Promesa
A Promise
maneja un único evento cuando una operación asíncrona se completa o falla.
Nota: Existen Promise
bibliotecas que admiten la cancelación, pero ES6 Promise
no lo hace hasta ahora.
Observable
An Observable
es como a Stream
(en muchos idiomas) y le permite pasar cero o más eventos donde se llama a la devolución de llamada para cada evento.
A menudo Observable
se prefiere Promise
porque ofrece las características de Promise
y más. No Observable
importa si desea manejar 0, 1 o varios eventos. Puede utilizar la misma API en cada caso.
Observable
También tiene la ventaja Promise
de ser cancelable . Si el resultado de una solicitud HTTP a un servidor o alguna otra operación asincrónica costosa ya no es necesario, un le Subscription
permite Observable
cancelar la suscripción, mientras que Promise
eventualmente llamará a la devolución de llamada exitosa o fallida incluso cuando no necesite el notificación o el resultado que proporciona.
Mientras que a Promise
comienza inmediatamente, Observable
solo comienza si te suscribes. Es por eso que los Observables se llaman vagos.
Observable proporciona operadores como map
,,, ... similares a una matrizforEach
reduce
También hay operadores potentes como retry()
, o replay()
, ... que suelen ser bastante útiles.
Una lista de operadores enviados con rxjs
La ejecución diferida le permite crear una cadena de operadores antes de que se ejecute el observable mediante la suscripción, para realizar un tipo de programación más declarativa.
Ambos Promises
y Observables
nos proporcionan abstracciones que nos ayudan a lidiar con la naturaleza asincrónica de nuestras aplicaciones. Günter y @Relu señalaron claramente la diferencia entre ellos .
Dado que un fragmento de código vale más que mil palabras, veamos el siguiente ejemplo para entenderlo más fácilmente.
Gracias @Christoph Burgdorf por el fantástico artículo.
Angular usa Rx.js Observables en lugar de promesas para tratar con HTTP.
Suponga que está creando una función de búsqueda que debería mostrarle resultados instantáneamente a medida que escribe. Suena familiar, pero hay muchos desafíos que conlleva esa tarea.
- No queremos llegar al punto final del servidor cada vez que el usuario presiona una tecla. Debería inundarlos con una tormenta de solicitudes HTTP . Básicamente, solo queremos presionarlo una vez que el usuario haya dejado de escribir en lugar de cada pulsación de tecla.
- No llegue al punto final de búsqueda con los mismos parámetros de consulta para solicitudes posteriores.
- Lidiar con respuestas desordenadas. Cuando tenemos varias solicitudes en curso al mismo tiempo, debemos tener en cuenta los casos en los que regresan en un orden inesperado. Imaginemos que primero escribimos computadora , nos detenemos, sale una solicitud, escribimos auto , nos detenemos, sale una solicitud. Ahora tenemos dos solicitudes en vuelo. Desafortunadamente, la solicitud que contiene los resultados de la computadora regresa después de la solicitud que contiene los resultados del automóvil .
La demostración constará simplemente de dos archivos: app.ts
y wikipedia-service.ts
. Sin embargo, en un escenario del mundo real, lo más probable es que dividiéramos aún más las cosas.
A continuación se muestra una implementación basada en Promesa que no maneja ninguno de los casos extremos descritos.
wikipedia-service.ts
import { Injectable } from '@angular/core';
import { URLSearchParams, Jsonp } from '@angular/http';
@Injectable()
export class WikipediaService {
constructor(private jsonp: Jsonp) {}
search (term: string) {
var search = new URLSearchParams()
search.set('action', 'opensearch');
search.set('search', term);
search.set('format', 'json');
return this.jsonp
.get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search })
.toPromise()
.then((response) => response.json()[1]);
}
}
Estamos inyectando el Jsonp
servicio para realizar una solicitud GET en la API de Wikipedia con un término de búsqueda determinado. Observe que llamamos toPromise
para pasar de an Observable<Response>
a a Promise<Response>
. Finalmente terminaremos con a Promise<Array<string>>
como tipo de retorno de nuestro método de búsqueda.
app.ts
// check the plnkr for the full list of imports
import {...} from '...';
@Component({
selector: 'my-app',
template: `
<div>
<h2>Wikipedia Search</h2>
<input #term type="text" (keyup)="search(term.value)">
<ul>
<li *ngFor="let item of items">{{item}}</li>
</ul>
</div>
`
})
export class AppComponent {
items: Array<string>;
constructor(private wikipediaService: WikipediaService) {}
search(term) {
this.wikipediaService.search(term)
.then(items => this.items = items);
}
}
Aquí tampoco hay mucha sorpresa. Inyectamos nuestra WikipediaService
y exponemos su funcionalidad a través de un método de búsqueda en la plantilla. La plantilla simplemente se vincula a keyup y llama search(term.value)
.
Desenvolvemos el resultado de la Promesa que devuelve el método de búsqueda de WikipediaService y lo exponemos como una simple matriz de cadenas a la plantilla para que podamos *ngFor
recorrerlo y crear una lista para nosotros.
Vea el ejemplo de implementación basada en promesas en Plunker
Donde realmente brillan los observables
Cambiemos nuestro código para no dañar el punto final con cada pulsación de tecla, sino solo enviar una solicitud cuando el usuario dejó de escribir durante 400 ms.
Para revelar tales superpoderes, primero necesitamos obtener un Observable<string>
que contenga el término de búsqueda que ingresa el usuario. En lugar de vincularnos manualmente al evento keyup, podemos aprovechar la formControl
directiva de Angular. Para utilizar esta directiva, primero debemos importarla ReactiveFormsModule
a nuestro módulo de aplicación.
app.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { JsonpModule } from '@angular/http';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [BrowserModule, JsonpModule, ReactiveFormsModule]
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
Una vez importado, podemos usar formControl desde nuestra plantilla y configurarlo con el nombre "término".
<input type="text" [formControl]="term"/>
En nuestro componente, creamos una instancia de FormControl
from @angular/form
y la exponemos como un campo bajo el término de nombre de nuestro componente.
Detrás de escena, term expone automáticamente una Observable<string>
propiedad as valueChanges
a la que podemos suscribirnos. Ahora que tenemos un Observable<string>
, superar la entrada del usuario es tan fácil como llamar debounceTime(400)
a nuestro Observable
. Esto devolverá un nuevo valor Observable<string>
que solo emitirá un nuevo valor cuando no hayan llegado nuevos valores durante 400 ms.
export class App {
items: Array<string>;
term = new FormControl();
constructor(private wikipediaService: WikipediaService) {
this.term.valueChanges
.debounceTime(400) // wait for 400 ms pause in events
.distinctUntilChanged() // ignore if next search term is same as previous
.subscribe(term => this.wikipediaService.search(term).then(items => this.items = items));
}
}
Sería un desperdicio de recursos enviar otra solicitud para un término de búsqueda cuyos resultados nuestra aplicación ya muestra. Todo lo que tenemos que hacer para lograr el comportamiento deseado es llamar al distinctUntilChanged
operador inmediatamente después de llamar.debounceTime(400)
Vea el ejemplo de implementación observable en Plunker.
Para lidiar con respuestas desordenadas, consulte el artículo completo http://blog.thinktram.io/angular/2016/01/06/ Taking-advantage-of-observables-in-angular2.html
En la medida en que uso HTTP en Angular, estoy de acuerdo en que en los casos de uso normales no hay mucha diferencia al usar Observable sobre Promise. Ninguna de las ventajas es realmente relevante en la práctica. Espero poder ver algún caso de uso avanzado en el futuro :)
Aprende más
- https://angular-2-training-book.rangle.io/handout/observables/
- https://angular.io/tutorial/toh-pt6#observables
Tanto Promises como Observables nos ayudarán a trabajar con las funcionalidades asincrónicas en JavaScript. Son muy similares en muchos casos; sin embargo, todavía existen algunas diferencias entre los dos: las promesas son valores que se resolverán de asynchronous
manera similar a las llamadas HTTP. Por otro lado, los observables tratan de una secuencia de eventos asincrónicos . Las principales diferencias entre ellos se enumeran a continuación:
Promesa:
- tener una tubería
- normalmente sólo se utiliza con devolución de datos asíncronos
- no es fácil cancelar
Observable:
- son cancelables
- son reintentables por naturaleza, como reintentar y reintentarCuando
- transmitir datos en múltiples canalizaciones
- tener operaciones tipo matriz como mapa, filtro, etc.
- Se puede crear a partir de otras fuentes, como eventos.
- son funciones, que se podrían suscribir más adelante
Además, he creado la imagen gráfica a continuación para mostrar las diferencias visualmente:
Hay una desventaja de los Observables que faltan en las respuestas. Las promesas permiten utilizar las funciones async/await de ES7. Con ellos puedes escribir código asincrónico como si fuera una llamada a función sincrónica, por lo que ya no necesitas devoluciones de llamada. La única posibilidad que tienen los Observables de hacer esto es convertirlos en Promesas. Pero cuando las conviertes en Promesas, solo puedes volver a tener un valor de retorno:
async function getData(){
const data = await observable.first().toPromise();
//do stuff with 'data' (no callback function needed)
}
Lectura adicional: ¿Cómo puedo "esperar" en un Rx Observable?
Promesas
- Definición: le ayuda a ejecutar funciones de forma asincrónica y a utilizar sus valores de retorno (o excepciones), pero solo una vez cuando se ejecuta.
- No perezoso
- No cancelable (existen bibliotecas de Promise que admiten la cancelación, pero ES6 Promise no lo hace hasta ahora). Las dos decisiones posibles son
- Rechazar
- Resolver
- No se puede volver a intentar (las promesas deben tener acceso a la función original que devolvió la promesa para tener la capacidad de reintentar, lo cual es una mala práctica)
Observables
- Definición: le ayuda a ejecutar funciones de forma asincrónica y a utilizar sus valores de retorno en una secuencia continua ( varias veces ) cuando se ejecuta.
- Por defecto, es diferido ya que emite valores a medida que avanza el tiempo.
- Tiene muchos operadores, lo que simplifica el esfuerzo de codificación.
- Se puede usar un reintento de operador para reintentar cuando sea necesario, también si necesitamos reintentar el observable en función de algunas condiciones, se puede usar retryWhen .
Nota : Una lista de operadores junto con sus diagramas interactivos está disponible aquí en **RxMarbles.com **