2

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).

Bart van den Burg
  • 2,166
  • 4
  • 25
  • 42

3 Answers3

2

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

Fan Cheung
  • 10,745
  • 3
  • 17
  • 39
  • but i guess the POST will still be cancelled? and i won't have access to the `res` – Bart van den Burg Nov 28 '19 at 08:27
  • it will not. unless you unsubscribe immediately after subscribe. In fact even switchMap cannot cancel post. Chrome will only cancel http when multiple http is firing too fast – Fan Cheung Nov 28 '19 at 08:29
  • unsubscribing from an observable created with http will most definitely cancel the request. – Adrian Brand Nov 28 '19 at 08:38
  • Need to correct my comment, even unsubscribing immediately doesn't guarantee canceling of http if it is already fired in browser – Fan Cheung Nov 28 '19 at 08:59
  • Yes you will not have access to the res, if you want access to res too then there should be a different solution – Fan Cheung Nov 28 '19 at 11:39
1

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();
Sámal Rasmussen
  • 2,887
  • 35
  • 36
0

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;
}
Adrian Brand
  • 20,384
  • 4
  • 39
  • 60
  • it'll not cancel http. https://stackblitz.com/edit/rxjs-playground-test-m9ujv9 take a look at network tab – Fan Cheung Nov 28 '19 at 08:38
  • Commenting on the wrong answer but fetch is not the angular http client, check network tap here https://stackblitz.com/edit/angular-hyx9mh – Adrian Brand Nov 28 '19 at 08:48
  • Please try this out in your code `const sub = http.get('https://jsonplaceholder.typicode.com/todos/1').subscribe(val => {}); sub.unsubscribe();` – Fan Cheung Nov 28 '19 at 08:52
  • you are requesting http.get('/') not a valid one. Please also take a look at this https://stackoverflow.com/questions/31061838/how-do-i-cancel-an-http-fetch-request for how to cancel http and what kind of special treatment it needs. – Fan Cheung Nov 28 '19 at 08:53
  • The observable still cancelled, which is what started this discussion – Adrian Brand Nov 28 '19 at 11:27
  • Of course observable will be canceled upon unsubscribe, but that’s not what the question is about I think. What we are taking about is whether it will cancel POST request – Fan Cheung Nov 28 '19 at 11:30
  • @AdrianBrand shareReplay looks interesting, we're going to try that. One question tho, do I now still need to unsubscribe, or is the observable "complete" and automatically cleaned up? – Bart van den Burg Nov 29 '19 at 08:27