Convertir la promesse en observable

215

J'essaie d'enrouler ma tête autour d'observables. J'aime la façon dont les observables résolvent les problèmes de développement et de lisibilité. Comme je l'ai lu, les avantages sont immenses.

Les observables sur HTTP et les collections semblent être simples. Comment puis-je convertir quelque chose comme ça en motif observable.

Il s'agit de mon composant de service, pour fournir l'authentification. Je préférerais que cela fonctionne comme les autres services HTTP dans Angular2 - avec prise en charge des gestionnaires de données, d'erreurs et de complétion.

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

Toute aide ici serait très appréciée. La seule solution de rechange que j'avais était de créer l' EventEmitterart. Mais je suppose que c'est une façon terrible de faire les choses dans la section des services

Krishnan Sriram
la source

Réponses:

322

Si vous utilisez RxJS 6.0.0:

import { from } from 'rxjs';
const observable = from(promise);
Guillaume
la source
9
En utilisant 6.3.3, la fromméthode retourne observable mais envoie une promesse comme valeur aux abonnements. :(
Laxmikant Dange
1
Cette réponse est corrext pour RXJS 6+. J'ai essayé d'importer depuis operatorsvia "intuition" - j'avais tort.
VSO
119

essaye ça:

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

vous pouvez trouver une référence complète à l'opérateur fromPromise ici .

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

1 Exécution / conversion directe

Permet fromde convertir directement une promesse précédemment créée en une observable.

import { from } from 'rxjs';

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

observable$sera un observable à chaud qui relit efficacement la valeur des promesses pour les abonnés.

Le corps des promesses est en cours d'exécution ou a déjà été résolu lors de la création de l'observable. Si la promesse intérieure a été résolue, un nouvel abonné à l'observable obtiendra immédiatement sa valeur.

2 Exécution différée sur chaque abonnement

Utiliser deferavec une fonction d'usine de promesse comme entrée pour différer la création et la conversion d'une promesse en observable.

import { defer } from 'rxjs';

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

observable$sera un froid observable .

La différence avec from est deferqu'attend un abonné et crée ensuite une nouvelle promesse en appelant la fonction d'usine de promesse donnée. Ceci est utile lorsque vous souhaitez créer un observable mais que vous ne voulez pas que la promesse intérieure soit exécutée immédiatement. La promesse intérieure ne sera exécutée que lorsque quelqu'un souscrira à l'observable. Chaque abonné recevra également son propre nouvel observable.

3 De nombreux opérateurs acceptent directement les promesses

La plupart des opérateurs de RxJS qui combinent (par exemple merge, concat, forkJoin, combineLatest...) ou transformer observables (par exemple switchMap, mergeMap, concatMap, catchError...) accepter des promesses directement. Si vous utilisez l'un d'eux de toute façon, vous n'avez pas à utiliser frompour envelopper une promesse en premier (mais pour créer un observable froid, vous devrez peut-être encore utiliser defer).

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

Vérifiez la documentation ou l' implémentation pour voir si l'opérateur que vous utilisez accepte ObservableInputou 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 différence entre fromet deferdans un exemple: 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
la source
4
Je pense que la différence est capitale, merci de l'avoir souligné.
Starscream
1

Vous pouvez également utiliser un sujet et déclencher sa fonction next () à partir de la promesse. Voir l'exemple ci-dessous:

Ajouter du code comme ci-dessous (j'ai utilisé le service)

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

  }
}

Créer un utilisateur à partir du composant comme ci-dessous

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

Shivang Gupta
la source
Les sujets sont des machines de bas niveau. N'utilisez pas de sujets, sauf pour les cas où vous étendez rxjs.
polkovnikov.ph
Je donne juste une solution.
Shivang Gupta
Vous auriez pu au moins montrer new Observable(observer => { ... observer.next() ... })comment l'implémenter. Même s'il s'agirait d'une réimplémentation d'une fonction bien connue existante, il répondrait directement à la question et ne nuirait pas aux lecteurs.
polkovnikov.ph
1

Vous pouvez également utiliser le report . La principale différence est que la promesse ne sera pas résolue ou rejetée avec impatience.

Mateja Petrovic
la source
0

Vous pouvez ajouter un wrapper autour de la fonctionnalité de promesse pour renvoyer un observable à l'observateur.

  • Création d'un observable paresseux à l' aide de l' opérateur defer () qui vous permet de créer l'observable uniquement lorsque l'observateur s'abonne.
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
la source