Many developers wants to convert an Observable to a Promise in an Angular 13+ applications so that they can use the powerful async await feature of ES6+ JavaScript or for any other reason. Earlier RxJS used to provide a toPromise method which directly converts an Observable to a Promise. Now RxJS has deprecated the toPromise, and if you try to use it, you will get the message

@deprecated — Replaced with firstValueFrom and lastValueFrom . Will be removed in v8 which looks something like this

Since, toPromise is deprecated, we need to use any of the new alternatives firstValueFrom or lastValueFrom.

How to use firstValueFrom & lastValueFrom to convert an Observable to a Promise in Angular 13+ applications

RxJS 7 comes with a built-in function firstValueFrom which takes an Observable as its first argument and returns a Promise.

Using firstValueFrom & lastValueFrom using then & catch syntax

firstValueFrom code example:

import { Injectable } from '@angular/core';
import { from, Observable, firstValueFrom, lastValueFrom } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class RxJSService {

    myObservable: Observable<string> = from(['John', 'Jane', 'James'])

    constructor() {
        firstValueFrom(this.myObservable).then((data) => {
            console.log(data); // we get John
        }).catch((error) => {
            console.log(error)
        })
    }

}

Similarly we can use the lastValueFrom built-in RxJS function

lastValueFrom example code:

import { Injectable } from '@angular/core';
import { from, Observable, firstValueFrom, lastValueFrom } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class RxJSService {

    myObservable: Observable<string> = from(['John', 'Jane', 'James'])

    constructor() {
        lastValueFrom(this.myObservable).then((data) => {
            console.log(data); // we get James
        }).catch((error) => {
            console.log(error)
        })
    }

}

Error Handling firstValueFrom & lastValueFrom RxJS functions

If the Observable passed to the firstValueFrom or lastValueFrom functions do not emit any value, then the Promise will be rejected and we get EmptyError with a ‘no elements in sequence’ error message.

example code:

import { Injectable } from '@angular/core';
import { from, Observable, firstValueFrom, lastValueFrom, EmptyError } from 'rxjs';


@Injectable({ providedIn: 'root' })
export class RxJSService {

    myObservable: Observable<string> = from([])

    constructor() {
        firstValueFrom(this.myObservable).then((data) => {
            console.log(data);
        }).catch((error: EmptyError) => {
            console.log(error.message) // we get no elements in sequence
        })
    }

}

If you do not want to get an error, you can provide a default value as the second arugement to the firstValueFrom or lastValueFrom functions like this

...
 myObservable: Observable<string> = from([])

    constructor() {
        lastValueFrom(this.myObservable, { defaultValue: 'Joe' }).then((data) => {
            console.log(data);// we get Joe
        }).catch((error: EmptyError) => {
            console.log(error.message)
        })
    }
...

Using firstValueFrom and lastValueFrom with async, await & try and catch

async and await is another widely used way to handle promises in JavaScript. We can use async, await along with try and catch to handle errors

example code to catch an error:

import { Injectable } from '@angular/core';
import { from, Observable, firstValueFrom, lastValueFrom, EmptyError } from 'rxjs';


@Injectable({ providedIn: 'root' })
export class RxJSService {

    myObservable: Observable<string> = from([])
    userName!: string;

    constructor() {
        this.initApp()
    }

    async initApp() {
        try {
            this.userName = await firstValueFrom(this.myObservable)
        }
        catch (error: unknown) {
            console.log((error as EmptyError).message) // we get no elements in sequence
        }
    }

}

another thing to note is that, lastValueFrom function waits for the Observable to complete so that it gets the last value from the stream. If the Observable is active, it do not return any value until it completes. For example, the following lastValueFrom will never gets settled (neither gets resolved nor rejected)

import { Injectable } from '@angular/core';
import { Observable, lastValueFrom, timer } from 'rxjs';


@Injectable({ providedIn: 'root' })
export class RxJSService {
    
    myTimer: Observable<number> = timer(1000, 3000)

    constructor() {
        lastValueFrom(this.myTimer).then((data) => { console.log(data) })
    }

}

To handle such types of observables, make sure that you use an additional RxJS operator such as take in such cases

example:

myTimer: Observable<number> = timer(1000, 3000)
    constructor() {
        lastValueFrom(this.myTimer.pipe(take(3))).then((data) => { console.log(data) }) // we get 2
    }