Bart van den Burg
Bart van den Burg

Reputation: 2344

RxJS: Make sure "tap" is fired even after unsubscribe

How can I make sure the tap operator is called even if a subscription is unsubscribed? Imagine the following:

function doUpdate() {
    return this.http.post('somewhere', {})
        .pipe(
            tap(res => console.log(res))
        )
}

const sub = doUpdate().subscribe(res => /* do something with res */);
setTimeout(() => sub.unsubscribe(), 1000)

In this case, I just want to prevent the subscribe action from being executed, yet I want to make sure the console log is fired, even if the request took longer than 1000 milliseconds to execute (and in this particular case, I don't even want the POST to be cancelled either).

Upvotes: 2

Views: 1326

Answers (3)

Sámal Rasmussen
Sámal Rasmussen

Reputation: 3495

In Rxjs 7.4 tap now has three more subscribe handlers, so you can use it to get notified on subscribe, unsubscribe and finalize:

https://github.com/ReactiveX/rxjs/commit/eb26cbc4488c9953cdde565b598b1dbdeeeee9ea#diff-93cd3ac7329d72ed4ded62c6cbae17b6bdceb643fa7c1faa6f389729773364cc

So you can do:

  const subscription = subject
  .pipe(
    tap({
      subscribe: () => results.push('subscribe'),
      next: (value) => results.push(`next ${value}`),
      error: (err) => results.push(`error: ${err.message}`),
      complete: () => results.push('complete'),
      unsubscribe: () => results.push('unsubscribe'),
      finalize: () => results.push('finalize'),
    })
  )
  .subscribe();

Upvotes: 1

Fan Cheung
Fan Cheung

Reputation: 11345

use finalize() operator, although that will also get called when observable is completed

function doUpdate() {
    return this.http.post('somewhere', {})
        .pipe(
           finalize(() => console.log(res))
        )
}

some code to demonstrate the idea: https://stackblitz.com/edit/rxjs-playground-test-m9ujv9

Upvotes: 2

Adrian Brand
Adrian Brand

Reputation: 21638

Unsubscribing from a http request will definitely cancel the request, you could try shareReplay

function doUpdate() {
    return this.http.post('somewhere', {})
        .pipe(
            tap(res => console.log(res)),
            shareReplay()
        )
}

and if that doesn't work the passing the result into a subject

function doUpdate() {
    const subject = new Subject();
    this.http.post('somewhere', {})
        .pipe(
            tap(res => console.log(res))
        ).subscribe(subject);
    return subject;
}

Upvotes: 0

Related Questions