Reputation: 7609
I have the following function that:
Take a value from my store => Transform it to csv => Create a download link
downloadCSV() {
let href;
this.store$.select(MissionsStoreSelectors.selectedRoute).pipe(take(1)).subscribe((route) => {
if (route) {
const csv = this.csvManipulatorService.generateCSVFromJSON({filename: route.routeId, data: route.waypoints, columns : ['id', 'position', 'rotation']});
href = this.domSanitizer.bypassSecurityTrustUrl('data:text/csv,' + encodeURIComponent(csv));
}
});
return href;
}
(don't take care about the fact there is no guard, is simplified)
I thought the subscribe was not synchronous. But from this accepted answer:
How to get current value of State object with @ngrx/store?
it looks like it is not.
My question is is this going to work 100% of the time? Is the subscribe with a take always synchronous ?
Upvotes: 0
Views: 3761
Reputation: 550
Two answers
No. Subscribe with take is not always synchronous. What makes an observable async/sync is the producer of the emitted values, not the operators in the stream. Now, if you did not use take(1)
or unsubscribe from the Observable at some point, it's not a synchronicity issue you would have, but a memory leak. The take(1)
is important, but not because of synchronicity concerns.
In your case, you are using a function to create and subscribe to an observable that gets the latest data from the store, which is basically a BehaviorSubject in that it always returns the most recent values on subscribe, and then the updates after that. This is a synchronous action, and I see no reason why it wouldn't always work.
This is testable by creating a simple synchronous Observable, modifying a value, and then logging it out immediately.
https://stackblitz.com/edit/rxjs-ujs6u9
You could simplify this however and return the Observable itself instead of wrapping it in a function:
csvHref$ = this.store$.select(MissionsStoreSelectors.selectedRoute).pipe(
take(1),
map(route => {
if (!route) {
return null;
}
const csv = this.csvManipulatorService.generateCSVFromJSON({
filename: route.routeId,
data: route.waypoints,
columns : ['id', 'position', 'rotation']
});
return this.domSanitizer.bypassSecurityTrustUrl(`data:text/csv,${encodeURIComponent(csv)}`);
})
)
And then instead of calling it like a function, you just subscribe to it to get the value.
csvHref$.subscribe(href => doSomethingWithHref(href));
Based on your usage that may not be possible, but you should think of Observables as being functions already.
I recommend https://medium.com/@benlesh/learning-observable-by-building-observable-d5da57405d87 as a good read.
Upvotes: 2