[angular] Convert Promise to Observable

I am trying to wrap my head around observables. I love the way observables solve development and readability issues. As I read, benefits are immense.

Observables on HTTP and collections seem to be straight forward. How can I convert something like this to observable pattern.

This is from my service component, to provide authentication. I'd prefer this to work like other HTTP services in Angular2 - with support for data, error and completion handlers.

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

Any help here would be much appreciated. The only alternative solution I had was to create EventEmitters. But I guess that's a terrible way to do things in services section

This question is related to angular firebase rxjs firebase-authentication

The answer is


You can also use a Subject and trigger its next() function from promise. See sample below:

Add code like below ( I used service )

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

Create User From Component like below

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


You can add a wrapper around promise functionality to return an Observable to observer.

  • Creating a Lazy Observable using defer() operator which allows you to create the Observable only when the Observer subscribes.
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);
   }
)


try this:

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

you can find complete reference to fromPromise operator here.


You may also use defer. The main difference is that the promise is not going to resolve or reject eagerly.


1 Direct Execution / Conversion

Use from to directly convert a previously created Promise to an Observable.

import { from } from 'rxjs';

// getPromise() is called once, the promise is passed to the Observable
const observable$ = from(getPromise());

observable$ will be a hot Observable that effectively replays the Promises value to Subscribers.

It's a hot Observable because the producer (in this case the Promise) is created outside of the Observable. Multiple subscribers will share the same Promise. If the inner Promise has been resolved a new subscriber to the Observable will get its value immediately.

2 Deferred Execution On Every Subscribe

Use defer with a Promise factory function as input to defer the creation and conversion of a Promise to an Observable.

import { defer } from 'rxjs';

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

observable$ will be a cold Observable.

It's a cold Observable because the producer (the Promise) is created inside of the Observable. Each subscriber will create a new Promise by calling the given Promise factory function.

This allows you to create an observable$ without creating and thus executing a Promise right away and without sharing this Promise with multiple subscribers. Each subscriber to observable$ effectively calls from(promiseFactory()).subscribe(subscriber). So each subscriber creates and converts its own new Promise to a new Observable and attaches itself to this new Observable.

3 Many Operators Accept Promises Directly

Most RxJS operators that combine (e.g. merge, concat, forkJoin, combineLatest ...) or transform observables (e.g. switchMap, mergeMap, concatMap, catchError ...) accept promises directly. If you're using one of them anyway you don't have to use from to wrap a promise first (but to create a cold observable you still might have to use defer).

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

Check the documentation or implementation to see if the operator you're using accepts ObservableInput or 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>;

The difference between from and defer in an example: 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);

import { from } from 'rxjs';

from(firebase.auth().createUserWithEmailAndPassword(email, password))
.subscribe((user: any) => {
      console.log('test');
});

Here is a shorter version using a combination of some of the answers above to convert your code from a promise to an observable.


Examples related to angular

error NG6002: Appears in the NgModule.imports of AppModule, but could not be resolved to an NgModule class error TS1086: An accessor cannot be declared in an ambient context in Angular 9 TS1086: An accessor cannot be declared in ambient context @angular/material/index.d.ts' is not a module Why powershell does not run Angular commands? error: This is probably not a problem with npm. There is likely additional logging output above Angular @ViewChild() error: Expected 2 arguments, but got 1 Schema validation failed with the following errors: Data path ".builders['app-shell']" should have required property 'class' Access blocked by CORS policy: Response to preflight request doesn't pass access control check origin 'http://localhost:4200' has been blocked by CORS policy in Angular7

Examples related to firebase

How can I solve the error 'TS2532: Object is possibly 'undefined'? Getting all documents from one collection in Firestore FirebaseInstanceIdService is deprecated Failed to resolve: com.google.firebase:firebase-core:16.0.1 NullInjectorError: No provider for AngularFirestore Firestore Getting documents id from collection How to update an "array of objects" with Firestore? firestore: PERMISSION_DENIED: Missing or insufficient permissions Cloud Firestore collection count iOS Swift - Get the Current Local Time and Date Timestamp

Examples related to rxjs

Angular - "has no exported member 'Observable'" What is pipe() function in Angular How to convert Observable<any> to array[] TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable What is the difference between Subject and BehaviorSubject? Best way to import Observable from rxjs take(1) vs first() Observable Finally on Subscribe Getting an object array from an Angular service BehaviorSubject vs Observable?

Examples related to firebase-authentication

Convert Promise to Observable Class file for com.google.android.gms.internal.zzaja not found How do I detect if a user is already logged in Firebase?