Comments (4)
This tricked me for a few minutes. This isn't a bug, it's expected behavior.
finalize
will ALWAYS be called whether the source is completed, errored, or unsubscribed. In this case, the finalize
callback is called synchronously during the unsubscription to the first observable, causing value 3
to fall through.
What you're looking for is tap({ complete: () => { /* do stuff */ } })
not finalize
... finalize
is like a "finally" block. It's GUARANTEED to be called no matter what. This is useful for tearing things down or logging.
from rxjs.
Hi @benlesh thanks for the response. I think I'm a bit confused or not totally following the explanation. I do expect the finalize
for 2
to emit. What I did not expect was that 2
value comes out of the pipe to the subscriber and logs next emit for 2
. Shouldn't a new value coming into the switchMap
cause the previous inner subscriber to always unsubscribe?
from rxjs.
const sub = new Subject<number>();
const finalizations = new Subject<number>();
sub
.pipe(
switchMap((id) => {
return from(Promise.resolve(id)).pipe(
finalize(() => {
console.log(`finalize for ${id}`);
finalizations.next(id);
})
);
})
)
.subscribe((id) => {
console.log(`next emit for ${id}`);
});
finalizations.pipe(filter((id) => id === 1)).subscribe(() => {
sub.next(3);
});
sub.next(1);
sub.next(2);
sub.next(1)
- Synchronously call
switchMap
's mapping function with1
- Which maps into the
from(Promise.resolve(1)).pipe(finalize(STUFF))
observable, we'll call thatinnerObservable1
. At this point the promise has already scheduled a microtask to emit on. switchMap
synchronously subscribes toinnerObservable1
. Nothing else happens, as we have to wait for the promise to resolve.sub.next(2)
, again synchronously. (We don't get back to this point for a bit)- We unsubscribe from the subscription to
innerObservable1
. This triggers thefinalize
callback!. - The
finalize
callback logsfinalize for 1
, then callsfinalizations.next(1)
synchronously. - The value 1 passes the
filter
in our second observable, and then synchronously hits the next handler in the subscription, callingsub.next(3)
. - We don't have to unsubscribe any inner subscriptions, because we already did that on 6.
- Synchronously call the
switchMap
's mapping function with3
. - Which maps into the
from(Promise.resolve(3)).pipe(finalize(STUFF))
observable, we'll call thatinnerObservable3
. At this point the promise has already scheduled a microtask to emit on. switchMap
sends the value3
from the resolved promise inside ofinnerObservable3
to the consumer loggingnext emit for 3
.innerObservable3
completes, triggering thefinalize
. Which logsfinalize for 3
, then callsfinalizations.next(3)
.3
doesn't pass thefilter
in the other subscription, so nothing is done there.- Back to the step after 5 above,
innerObservable3
has cleaned up, so there's no inner subscription to unsubscribe from, and we callswitchMap
's mapping function with the value2
. - Which maps into the
from(Promise.resolve(2)).pipe(finalize(STUFF))
observable, which we'll callinnerObservable2
. At this point the promise has already scheduled a microtask to emit on. - We synchronously subscribe to
innerObservable2
. - When the promise resolves, we emit
2
to the consumer, loggingnext emit for 2
. - Then
innerObservable2
completes, callingfinalize
's callback. - The
finalize
callback synchronously logsfinalize for 2
, then callsfinalizations.next(2)
. 2
does not pass the filter so nothing happens in the other observable.
from rxjs.
Moral of the story: Don't do too much synchronous stuff in observables, it's really weird. :P
from rxjs.
Related Issues (20)
- TestScheduler expectObservable not respecting subscription marbles when using toEqual
- pipe expected 0 arguments HOT 5
- Incorrect @deprecated for fromEvent overloads HOT 2
- test issue
- test issue
- rxjs.dev very often inaccessible HOT 10
- export map means node always resolves CJS HOT 7
- RxJs ships generators instead of native async/await HOT 1
- bindCallback: resultSelector destructuring callback argument if it's array HOT 1
- `every` operator sending multiple values when re-entrant HOT 1
- [email protected] has peer deps?! Oops
- ObservableInput<T> should support Thennable<T>, not PromiseLike<T> HOT 1
- Delay, timer and possibly other operators have a maximum milliseconds value, even when date is passsed HOT 1
- lost stacktrace after promise is resolved HOT 1
- Teardown and error/complete messages are out of order HOT 2
- Issue with catchError HOT 1
- False positive in deprecation with mergMap HOT 1
- it's possible to return an array in catchError() HOT 2
- tap-complete not called for last observable inside zip HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from rxjs.