user
user

Reputation: 568

Cancel flatMap observable call chain

I am using angular 5 and rxjs. I am making 2 service calls, one dependent on others results. I am doing it using flatMap. I am also using takeUntil so that I can abort operation at any given point. My code looks like below:

this.myservice.api1(param1).pipe(takeUntil(this.destroyed$), finalize(() => {
//do something after both api calls are completed
},
flatMap((result1) => {
    //do some operation and create object x(this.objx)
    return this.myservice.api2(param1);
})
).subscribe((result2) => {
    //do something based on result2 and this.objx
})

This code is executed in for loop and loop gets executed for 200 times. Therefore 400 network calls are made. I have a button on UI to abort this operation and this.destroyed$ observable gets executed when it is clicked. I can abort this way for any iteration where second API call is not made(api2). If only first API is getting called, that request gets cancelled. If api2 has been called, which takes 20-30 seconds to respond, it doesn't get cancelled. I want both API calls to be cancelled. Is it possible?

Upvotes: 4

Views: 1204

Answers (1)

martin
martin

Reputation: 96889

You can just restructure your chain and put takeUntil at the end.

this.myservice.api1(param1).pipe(
  mergeMap((result1) => { // flatMap is just alias for `mergeMap` which is the recommended name you should use
    //do some operation and create object x(this.objx)
    return this.myservice.api2(param1);
  }),
  takeUntil(this.destroyed$),
  finalize(() => {
    //do something after both api calls are completed
  },
).subscribe((result2) => {
  //do something based on result2 and this.objx
});

The problem you had is in how mergeMap works internally (this is on purpose). mergeMap merges its source Observable and all inner Observables. So if you put takeUntil before mergeMap you're just completing the source Observable while all inner Observables are still active.

If you put it after mergeMap it will make the observer to complete which triggers dispose handlers bottom-up order and when mergeMap is unsubscribed it unsubscribes from its inner Observables.

This is the relevant line: https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/mergeMap.ts#L147

Also have a look at this article from one of RxJS Core Devs: https://ncjamieson.com/avoiding-takeuntil-leaks/

Upvotes: 5

Related Questions