How to use await in RxJS

Viewed 66

I am very new in RxJS

Here is my function

getData(id): Observable<any> {

    let accessToken = this.getAccessHeader();

    if (!accessToken) {
        // Here I want to wait till following observable completes, and returns value. Till then do not call next line methods i.e. code outside the if block

        this.getTemptoken().subscribe((res) => {
            accessToken = res.result.accessToken,
        });
    }
    const httpOptions = {
        headers: new HttpHeaders({
            'accessToken': accessToken
        })
    };
    return this.http.post(
        url,
        {
            'id': id
        }, httpOptions
    ).pipe(map((res) => {
        return res;
    }));
}

getTemptoken is also an observable, similar function, which calls API and returns token. I want to wait till getTemptoken returns value and after that only execute next lines, i.e. code below if block

2 Answers

To use the result of the first observable in the subsequent observable you can use switchMap in a pipe, e.g.:

of("Hello").pipe(
            switchMap(result => of(result + ", World")),
            switchMap(result => of(result + "!")),
          )
          .subscribe(console.log);
// output: Hello, World!

learnrxjs switchmap


If you don't need the result of the previous observable and just want to trigger observables in a specific order, you can use concat which waits for every observable to complete before triggering the next observable:

concat(
  of(1).pipe(delay(2000)),
  of(2).pipe(delay(500)),
  of(3).pipe(delay(1000))
)
.subscribe(console.log);

// output: 1 2 3

learnrxjs concat


Additional you can use toPromise(), but it's an AntiPattern. Have a look at

RxJS Observable interop with Promises and Async-Await

E.g.:

const source$ = Observable.interval(1000).take(3); // 0, 1, 2
// waits 3 seconds, then logs "2".
// because the observable takes 3 seconds to complete, and 
// the interval emits incremented numbers starting at 0
async function test() {
  console.log(await source$.toPromise());
}

learnrxjs topromise

This is how I would rewrite your method to work as you want it.

getData(id): Observable<any> {
  let accessToken$ = of(this.getAccessHeader())
    .pipe(
      switchMap(accessToken => accessToken ? of(accessToken) : this.getTemptoken()
        .pipe(
          map(res => res.result.accessToken)
        )
      )
    )

  return accessToken$
    .pipe(
      map(accessToken => {
        headers: new HttpHeaders({
          'accessToken': accessToken
        })
      }),
      switchMap(httpOptions => this.http.post(url, { id }, httpOptions)),
    )
}

accessToken$ is responsible for getting accesToken in case this.getAccessHeader() doesn't give us what we want. This is happening inside switchMap, if we have accessToken we return of(accessToken), else we are returning this.getTemptoken().

Then we take accessToken$, turn it into httpOptions and via switchMap we return our desired this.http.post. Finally whoever subscribes to this method will get desired http response.

Related