Convertir promesa a observable

215

Estoy tratando de entender mis observables. Me encanta la forma en que los observables resuelven los problemas de desarrollo y legibilidad. Mientras leo, los beneficios son inmensos.

Los observables en HTTP y las colecciones parecen ser sencillos. ¿Cómo puedo convertir algo como esto en un patrón observable?

Esto es de mi componente de servicio, para proporcionar autenticación. Prefiero que esto funcione como otros servicios HTTP en Angular2, con soporte para manejadores de datos, errores y finalización.

firebase.auth().createUserWithEmailAndPassword(email, password)
  .then(function(firebaseUser) {
    // do something to update your UI component
    // pass user object to UI component
  })
  .catch(function(error) {
    // Handle Errors here.
    var errorCode = error.code;
    var errorMessage = error.message;
    // ...
  });

Cualquier ayuda aquí sería muy apreciada. La única solución alternativa que tuve fue crear EventEmitters. Pero supongo que es una forma terrible de hacer cosas en la sección de servicios

Krishnan Sriram
fuente

Respuestas:

321

Si está utilizando RxJS 6.0.0:

import { from } from 'rxjs';
const observable = from(promise);
Guillaume
fuente
9
Usando 6.3.3, el frommétodo devuelve observable pero está enviando promesa como valor a las suscripciones. :(
Laxmikant Dange
1
Esta respuesta es corrext para RXJS 6+. Traté de importar a operatorstravés de "intuición", me equivoqué.
VSO
119

prueba esto:

import 'rxjs/add/observable/fromPromise';
import { Observable } from "rxjs/Observable";

const subscription = Observable.fromPromise(
    firebase.auth().createUserWithEmailAndPassword(email, password)
);
subscription.subscribe(firebaseUser => /* Do anything with data received */,
                       error => /* Handle error here */);

Puede encontrar una referencia completa del operador fromPromise aquí .

Padrino
fuente
47
import 'rxjs/add/observable/fromPromise';
Simon Briggs
16
import { Observable } from "rxjs/Observable"; :)
Luckylooke
41

1 Ejecución directa / conversión

Utilícelo frompara convertir directamente una promesa creada previamente en una observable.

import { from } from 'rxjs';

// getPromise() will only be called once
const observable$ = from(getPromise());

observable$será un observable atractivo que reproduce efectivamente el valor de las promesas para los suscriptores.

El cuerpo de promesas se está ejecutando o ya se ha resuelto cuando se crea el observable. Si la promesa interna se ha resuelto, un nuevo suscriptor al observable obtendrá su valor de inmediato.

2 Ejecución diferida en cada suscripción

Úselo defercon una función de fábrica de promesas como entrada para diferir la creación y conversión de una promesa a una observable.

import { defer } from 'rxjs';

// getPromise() will be called every time someone subscribes to the observable$
const observable$ = defer(() => getPromise());

observable$Será un frío observable .

La diferencia fromes que deferespera a un suscriptor y solo entonces crea una nueva promesa llamando a la función de fábrica de promesa dada. Esto es útil cuando desea crear un observable pero no desea que la promesa interna se ejecute de inmediato. La promesa interna solo se ejecutará cuando alguien se suscriba a lo observable. Cada suscriptor también obtendrá su propio nuevo observable.

3 Muchos operadores aceptan promesas directamente

La mayoría de los operadores RxJS que combinan (por ejemplo merge, concat, forkJoin, combineLatest...) o transformar observables (por ejemplo switchMap, mergeMap, concatMap, catchError...) aceptar promesas directamente. Si está utilizando uno de ellos de todos modos, no tiene que usarlo frompara envolver una promesa primero (pero para crear un observable en frío aún podría tener que usarlo defer).

// Execute two promises simultaneously
forkJoin(getPromise(1), getPromise(2)).pipe(
  switchMap(([v1, v2]) => v1.getPromise(v2)) // map to nested Promise
)

Consulte la documentación o implementación para ver si el operador que está utilizando acepta ObservableInputo SubscribableOrPromise.

type ObservableInput<T> = SubscribableOrPromise<T> | ArrayLike<T> | Iterable<T>;
// Note the PromiseLike ----------------------------------------------------v
type SubscribableOrPromise<T> = Subscribable<T> | Subscribable<never> | PromiseLike<T> | InteropObservable<T>;

La diferencia entre fromy deferen un ejemplo: https://stackblitz.com/edit/rxjs-6rb7vf

const getPromise = val => new Promise(resolve => {
  console.log('Promise created for', val);
  setTimeout(() => resolve(`Promise Resolved: ${val}`), 5000);
});

// the execution of getPromise('FROM') starts here, when you create the promise inside from
const fromPromise$ = from(getPromise('FROM'));
const deferPromise$ = defer(() => getPromise('DEFER'));

fromPromise$.subscribe(console.log);
// the execution of getPromise('DEFER') starts here, when you subscribe to deferPromise$
deferPromise$.subscribe(console.log);
fridoo
fuente
44
Creo que la diferencia es capital, gracias por señalarlo.
Starscream
1

También puede usar un Asunto y activar su función next () desde la promesa. Ver muestra a continuación:

Agregue el código como a continuación (utilicé el servicio)

class UserService {
  private createUserSubject: Subject < any > ;

  createUserWithEmailAndPassword() {
    if (this.createUserSubject) {
      return this.createUserSubject;
    } else {
      this.createUserSubject = new Subject < any > ();
      firebase.auth().createUserWithEmailAndPassword(email,
          password)
        .then(function(firebaseUser) {
          // do something to update your UI component
          // pass user object to UI component
          this.createUserSubject.next(firebaseUser);
        })
        .catch(function(error) {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          this.createUserSubject.error(error);
          // ...
        });
    }

  }
}

Crear usuario desde componente como a continuación

class UserComponent {
  constructor(private userService: UserService) {
    this.userService.createUserWithEmailAndPassword().subscribe(user => console.log(user), error => console.log(error);
    }
  }

Shivang Gupta
fuente
Los sujetos son maquinaria de bajo nivel. No use sujetos, excepto en los casos en los que está extendiendo rxjs.
polkovnikov.ph
Solo estoy dando una solución.
Shivang Gupta
Al menos podría haber mostrado la new Observable(observer => { ... observer.next() ... })forma de implementarlo. Aunque sería una reimplementación de la función conocida existente, respondería directamente a la pregunta y no sería perjudicial para los lectores.
polkovnikov.ph
1

También puede usar diferir . La principal diferencia es que la promesa no se va a resolver o rechazar con entusiasmo.

Mateja Petrovic
fuente
0

Puede agregar un contenedor alrededor de la funcionalidad de promesa para devolver un Observable al observador.

  • Creando un perezoso Observable usando el operador defer () que le permite crear el Observable solo cuando el Observador se suscribe.
import { of, Observable, defer } from 'rxjs'; 
import { map } from 'rxjs/operators';


function getTodos$(): Observable<any> {
  return defer(()=>{
    return fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then(response => response.json())
      .then(json => {
        return json;
      })
  });
}

getTodos$().
 subscribe(
   (next)=>{
     console.log('Data is:', next);
   }
)

khizer
fuente