Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is finalize lifted in RxJS?

Tags:

rxjs

I am having trouble understanding the finalize operator in RxJS. Let me demonstrate this on an example:

of(null).pipe(
  tap({ complete: () => console.log('tap 1 completes') }),
  finalize(() => console.log('finalize')),
  tap({ complete: () => console.log('tap 2 completes') })
).subscribe({ complete: () => console.log('subscribe completes') });

I would expect the finalize callback to be executed before the second tap. That's not happening, though. Rather the above code produces the following output:

tap 1 completes
tap 2 completes
subscribe completes
finalize

Looking at the implementation I believe the operator is passed (lifted) through the whole observable chain to always be applied at its end. So now I end up with two questions:

  1. What's the rationale behind this design decision? Can you give some explanation on why this is a desireable / advantageous property?
  2. Is there a different operator or other solution to execute code on complete and on error, but in order (i.e. before the second tap in the above example) rather than at the end of the observable chain?
like image 434
kremerd Avatar asked Jan 24 '23 21:01

kremerd


1 Answers

It's important to be aware that finalize() and tap() work very differently. tap() is triggered by next, error and complete notifications while finalize() is only triggerd on chain ubsubscription. In other words finalize() is very similar to using:

const subscription = $source.subscribe();
// This will be always triggered after all `tap()`s
subscription.add(() => console.log('same as finalize()'));

So you can't make finalize() to be invoked before tap(). Also, be aware that finalize() is invoked also when you manually unsubscribe liek the following:

subscription.unsubscribe(); // will always invoke `finalize()` but never `tap()`

One possible solution could be implementing you own finalize() variant that knows the reason why it's being called: https://github.com/martinsik/rxjs-extra/blob/master/doc/finalizeWithReason.md (see source code)

Also note, that https://github.com/ReactiveX/rxjs/pull/5433 will have no affect on your use-case.

like image 100
martin Avatar answered Feb 27 '23 10:02

martin