Dave
Dave

Reputation: 5416

Angular 2: Convert Observable to Promise

Q) How do I convert the following observable to a promise so I can call it with .then(...)?

My method I want to convert to a promise:

  this._APIService.getAssetTypes().subscribe(
    assettypes => {
        this._LocalStorageService.setAssetTypes(assettypes);
    },
    err => {
        this._LogService.error(JSON.stringify(err))
    },
    () => {}
  ); 

The service method it calls:

  getAssetTypes() {
    var method = "assettype";
    var url = this.apiBaseUrl + method;

    return this._http.get(url, {})
      .map(res => <AssetType[]>res.json())
      .map((assettypes) => {
        assettypes.forEach((assettypes) => {
          // do anything here you might need....
      });
      return assettypes;
    });      
  }  

Thanks!

Upvotes: 96

Views: 169630

Answers (9)

evilkos
evilkos

Reputation: 597

As most mentioned, you can simply use myObservable.toPromise(). However, it's going to be removed in the next version of rxJs. For those who want to keep using toPromise() however (perhaps to avoid changing it throughout multiple projects they own), you can create an 'extension method' of sorts, to have 'your own' toPromise() that won't be deprecated. You can use this now even before it gets removed from rxJs.

declare module "rxjs" {
    interface Observable<T> {
        /**
         * Extension method. Applies 'lastValueFrom' to Observable<T>.
         */
        toPromise(): Promise<T | undefined>;
    }
}

Observable.prototype.toPromise = function <T>(this: Observable<T>): Promise<T> {
    return lastValueFrom(this);
};

To get this to show up everywhere, you must put this in the file that bootstraps your application. For Angular apps, that can be main.js.

Upvotes: 0

Luca C.
Luca C.

Reputation: 12564

observable can be converted to promise like this:

import { firstValueFrom, lastValueFrom } from 'rxjs';
...
lastValueFrom(observable).then(lastValue=>...);
firstValueFrom(observable).then(firstValue=>...);

toPromise() was the previous solution, deprecated from RxJS 7, it was:

let promise=observable.toPromise();


 

Upvotes: 23

Divek John
Divek John

Reputation: 755

I like it raw so this one since toPromise() is no more

   const status = await new Promise<boolean>((resolve, reject) => {
     someObs$.subscribe({
      next: resolve,
      error: reject,
    });
  });

A sophisticated way is using https://rxjs.dev/api/index/function/lastValueFrom

  const replyTo = new AsyncSubject();

  replyTo.next(false);
  replyTo.next(false);
  replyTo.next(true);

  replyTo.complete();

  const status = await lastValueFrom(replyTo) // true

Upvotes: 0

Emeric
Emeric

Reputation: 6885

Edit:

.toPromise() is now deprecated in RxJS 7 (source: https://rxjs.dev/deprecations/to-promise)

New answer:

As a replacement to the deprecated toPromise() method, you should use one of the two built in static conversion functions firstValueFrom or lastValueFrom.

Example:

import { interval, lastValueFrom } from 'rxjs';
import { take } from 'rxjs/operators';
 
async function execute() {
  const source$ = interval(2000).pipe(take(10));
  const finalNumber = await lastValueFrom(source$);
  console.log(`The final number is ${finalNumber}`);
}
 
execute();
 
// Expected output:
// "The final number is 9"

Old answer:

A lot of comments are claiming toPromise deprecated but as you can see here it's not.

So please juste use toPromise (RxJs 6) as said:

//return basic observable
const sample = val => Rx.Observable.of(val).delay(5000);
//convert basic observable to promise
const example = sample('First Example')
  .toPromise()
  //output: 'First Example'
  .then(result => {
    console.log('From Promise:', result);
  });

async/await example:

//return basic observable
const sample = val => Rx.Observable.of(val).delay(5000);
//convert basic observable to promise
const example = await sample('First Example').toPromise()
// output: 'First Example'
console.log('From Promise:', result);

Read more here.


Note: Otherwise you can use .pipe(take(1)).toPromise but as said you shouldn't have any problem using above example.

Upvotes: 11

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657058

rxjs7

lastValueFrom(of('foo'));

https://indepth.dev/posts/1287/rxjs-heads-up-topromise-is-being-deprecated

rxjs6

https://github.com/ReactiveX/rxjs/issues/2868#issuecomment-360633707

Don't pipe. It's on the Observable object by default.

Observable.of('foo').toPromise(); // this

rxjs5

import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/map';

...

this._APIService.getAssetTypes()
.map(assettypes => {
  this._LocalStorageService.setAssetTypes(assettypes);
})
.toPromise()
.catch(err => {
  this._LogService.error(JSON.stringify(err));
});

Upvotes: 143

Nikunj Kakadiya
Nikunj Kakadiya

Reputation: 2998

You can convert Observable to promise just by single line of code as below:

let promisevar = observable.toPromise()

Now you can use then on the promisevar to apply then condition based on your requirement.

promisevar.then('Your condition/Logic');

Upvotes: 0

Alf Moh
Alf Moh

Reputation: 7407

toPromise is deprecated in RxJS 7.

Use:

  1. lastValueFrom

Used when we are interested in the stream of values. Works like the former toPromise

Example

public async getAssetTypes() {
  const assetTypes$ = this._APIService.getAssetTypes()
  this.assetTypes = await lastValueFrom(assetTypes$);
}
  1. firstValueFrom

Used when we are not interested in the stream of values but just the first value and then unsubscribe from the stream

public async getAssetTypes() {
  const assetTypes$ = this._APIService.getAssetTypes()
  this.assetTypes = await firstValueFrom(assetTypes$); // get first value and unsubscribe
}

Upvotes: 5

Teodor Hirs
Teodor Hirs

Reputation: 489

The proper way to make Observable a Promise, in your case would be following

getAssetTypesPromise() Observable<any> {
  return new Promise((resolve, reject) => {
      this.getAssetTypes().subscribe((response: any) => {
        resolve(response);
      }, reject);
    });
}

Upvotes: 15

danday74
danday74

Reputation: 56936

you dont really need to do this just do ...

import 'rxjs/add/operator/first';


this.esQueryService.getDocuments$.first().subscribe(() => {
        event.enableButtonsCallback();
      },
      (err: any) => console.error(err)
    );
    this.getDocuments(query, false);

first() ensures the subscribe block is only called once (after which it will be as if you never subscribed), exactly the same as a promises then()

Upvotes: 13

Related Questions