Is it safe/okay to combine take(1) and defaultIfEmpty when subscribing to Observable<boolean>? - rxjs

I am using the following code to basically ensure that I get a result from an Observable
this.authenticationService.isLoggedIn
.pipe(
take(1),
defaultIfEmpty(false)
)
.subscribe(result => return result);
Somehow it feels wrong to me, maybe because it seems sort of procedural.
Is this method okay? Will this get me in trouble in any way?

If in your code it's fine that this.authenticationService.isLoggedIn completes without an emit - then the code in your question is fine too.
If this.authenticationService.isLoggedIn emits anything at some point of time and completes after - then defaultIfEmpty is redundant.

It all depends on what isLoggedIn does.
It is clear that isLoggedIn returns an Observable.
Now, and Observable can do just 3 things
it can notify, i.e. emit, some data for consumption of Observers which are subscribed
it can raise an error
it can complete
So the first question is: how many times can isLoggedIn notify? Is it just one shot or is it a stream of notifications? If it can emit just one value and then complete, than the take(1) operator is useless.
But there is also the case that isLoggedIn never notifies and just completes. In this case a notification would never be signaled by the observer to its subscriber. Using defaultIfEmpty operator ensures that something is notified even in this case.
So, reading your code I understand that isLoggedIn can behave in these 2 ways
Emit more than once but you are interested only in the first notification
Never notify and just complete, in which case you want false to be returned
If this is not true, it may be the case that your code can be simplified.

Related

Do RxJS observers always process all events submitted before the observable is completed?

I want to make sure that all events, which were submitted before complete was invoked on the observable, are logged.
I'm aware that operators exist that stop emission of events (takeUntil, etc.) completely.
The question that I have is whether other operators exist which would lead to emissions not being sent if the complete on the subject is sent too 'early'. Are there cases where it would be beneficial to wait with the completion of the observable until the event was handled by the observer?
For example, are there situations (imagine any other RxJS operator instead of the delay) where the following code ...
const subj = new Subject<string>();
subj.pipe(delay(500))
.subscribe((val) => {
console.log(val);
subj.complete();
});
subj.next('1');
... makes more sense than that ...
const subj = new Subject<string>();
subj.pipe(delay(500))
.subscribe((val) => {
console.log(val);
});
subj.next('1');
subj.complete();
... when the subject should only emit one value?
Or is completing the subject immediately after next always safe in such situations?
If there are other factors I'm not aware of (e.g. synchronous vs. asynchronous execution of code) please mention them as well.
In general,
subj.next('1');
subj.complete();
is safe. As far as I know, none of the existing RxJS operators use a completion to cancel/unsubscribe observables early.
That being said, I can easily create such an operator myself. switchMap will cancel currently running inner observables when it receives it's next emission. You could, for example, create a custom operator that unsubscribes and exists early when it receives a complete event.
If your worried about that, however, you're out of luck. No matter what workaround you imagine, I can probably write an operator that will ruin your code. For example filter(_ => false) will stop the 1 from being emitted before the complete in either of the two cases you've described.
In the end, you and your coworkers must write good code (and test!) and RxJS doesn't change that.

Is there a way to subscribe the same value all time without subscribe break?

I am subscribing a value from a reducer, where it will always true. when first time i subscribes it works. but next time it's not. from my research always the value is true if i change value to false then it works fine.
so subscribe expect the different value to observer or unsubscription. at present I do like this:
this.store.dispatch(new actionsMc.CheckConcurrencySuccess(false));
this.store.dispatch(new actionsMc.CheckConcurrency(true));
even though I required to pass true always i do above to subscribe my update. instead of this work around any other way is there is rxjs any one help me?
in subscription i tried with take once but it not working further. but looking for some other work around.
here is my subscription:
this.store.pipe(last(select(subscribe.newvalue))).subscribe(res => {
console.log('new res', res);
})
but not works. as a simple how to subscribe and unsubscribe on each value received?
If I understood you correctly, you are saying that even though the data in the store is updated to "true", you are not not notified about it in your subscription. That is because store automatically compares the new and the previous values, and fires .next() only if the values are not equal.
The simplest solution for you would be to wrap the boolean field in your store into an object (or array: [boolean] instead of boolean), so you would get notified every time it is updated.

Rxjs - How to retry an errored observable while informing UI of the error

Problem
Suppose there is a Http request observable that errored, we can just retry it. But I also want the UI to inform the user that this resource failed to load. What is the best architecture?
Intended Behavior for the Target Observable
Retry-able.
Long-running. Doesn't complete or error.
Shared. Does not generate unnecessary requests when multiple subscriber.
Load on need. Does not generate unnecessary requests when not subscribed.
Inform UI of the errors.
(3 and 4 can be achieved by shareReplay({bufferSize: 1, refCount: true}))
My Attempts
I think it's best to pass an error message to the downstream observer while keeping retrying the source. It causes minimum changes to the architecture. But I didn't see a way I can do it with Rxjs, because
retry() always intercepts the error. If you materialze the error, then retry() won't retry. If not, then no error will propagate to the downstream.
catchError() without rethrowing will always complete the stream.
Although let the UI observer tap(,,onError) and retry() can satisfy this need, but I think it is dangerous to let the UI take this responsibility. And multiple UI observer means a LOT of duplicated retries.
Well, I seem to have accidentally find the answer while browsing through the documentations.
It starts with the usage of the second parameter of the catchError. According to the documentation, retry is implemented by catchError. And we can express more logic with the lower-level catchError.
So it's just
catchError((err, caught) => {
return timer(RETRY_DELAY_TIME).pipe(
mergeMap(() => caught)
startWith(err)
);
})
It retries the observable, meanwhile sending error messages to the downstream observers. So the downstream is aware of the connection error, and can expect to receive retried values.
It sounds like you're looking for something akin to an NgRx side effect. You can encase it all in an outer Observable, piping the error handler to the inner Observable (your HTTP call), something like this:
const myObs$ = fromEvent('place event that triggers call here').pipe(
// just one example, you can trigger this as you please
switchMap(() => this.myHttpService.getResource().pipe(
catchError(err => handleAndRethrowError()),
retry(3)
),
shareReplay()
);
This way, if the request throws an error, it is retried 3 times (with error handling in the catchError block, and even if it fully errors out, the outer Observable is still alive. Does that look like it makes sense?

SwitchMap vs MergeMap in the #ngrx example

Below is code from the Ngrx example: https://github.com/ngrx/example-app/blob/master/src/effects/book.ts My question is why in the first #Effect, it uses switchMap while the others use mergeMap. Is that because the first #Effect is dealing with network, and with the switchMap you can cancel the previous network request if it's running?
#Effect() search$ = this.updates$
.whenAction(BookActions.SEARCH)
.map<string>(toPayload)
.filter(query => query !== '')
.switchMap(query => this.googleBooks.searchBooks(query)
.map(books => this.bookActions.searchComplete(books))
.catch(() => Observable.of(this.bookActions.searchComplete([])))
);
#Effect() clearSearch$ = this.updates$
.whenAction(BookActions.SEARCH)
.map<string>(toPayload)
.filter(query => query === '')
.mapTo(this.bookActions.searchComplete([]));
#Effect() addBookToCollection$ = this.updates$
.whenAction(BookActions.ADD_TO_COLLECTION)
.map<Book>(toPayload)
.mergeMap(book => this.db.insert('books', [ book ])
.mapTo(this.bookActions.addToCollectionSuccess(book))
.catch(() => Observable.of(
this.bookActions.addToCollectionFail(book)
))
);
#Effect() removeBookFromCollection$ = this.updates$
.whenAction(BookActions.REMOVE_FROM_COLLECTION)
.map<Book>(toPayload)
.mergeMap(book => this.db.executeWrite('books', 'delete', [ book.id ])
.mapTo(this.bookActions.removeFromCollectionSuccess(book))
.catch(() => Observable.of(
this.bookActions.removeFromCollectionFail(book)
))
);
}
You are correct; switchMap will unsubscribe from the Observable returned by its project argument as soon as it has invoked the project function again to produce a new Observable.
RxJs is incredibly powerful and dense, but its high level of abstraction can sometimes make code hard to understand. Let me debunk the marble diagrams and docs given by #Andy Hole a little and bring them up to date. You may find the marble syntax reference highly valuable to better understand rxjs operators from their tests (at least I found this missing/not highlighted enough in the official docs).
mergeMap
The first line in the diagram is the source Observable which emits (1,3,5) at different times. The second line in the diagram is the prototypical Observable returned by the project function i => ... passed to the .mergeMap() operator.
When the source Observable emits the item 1, mergeMap() invokes the project function with i=1. The returned Observable will emit 10 three times, every 10 frames (see marble syntax reference). The same happens when the source Observable emits item 3 and the project function creates an Observable that emits 30 three times. Note that the result of mergeMap() contains all three elements generated by each Observable returned from project.
switchMap
This is different with switchMap(), which will unsubscribe from the Observable returned by project as soon as it has invoked it again on a new element. The marble diagram indicates this with the missing third 30 item in the output Observable.
In the example you have given, this leads to the cancellation of the pending search request. This is a very nice but hard-to-get-right property, which you get for free by combining switchMap() with cancellable Observables returned by Angular's Http service. This can save you a lot of headaches without worrying about properly handling all the race conditions that typically occur with async cancellation.
You are right.
As you can see, switchMap is used with search functionality. The searchbox in this example is programmed to basically emit a search request when the user enters text in the textbox (with a 350ms debounce or delay).
This means that when the user enters 'har', ngrx sends a search request to the service. When the user enters another letter 'r', the previous request is canceled (since we are not interested in 'har' anymore, but 'harr').
It is very nicely shown in the marble diagrams provided in another answer.
In mergeMap, the previous Observables are not canceled and therefore '30' and '50' are mixed together. Using switchMap, only the 5s are emitted, because the 3's are canceled.
mergeMap
Projects each source value to an Observable which is merged in the output Observable.
Maps each value to an Observable, then flattens all of these inner Observables using mergeAll.
switchMap
Projects each source value to an Observable which is merged in the output Observable, emitting values only from the most recently projected Observable.
Maps each value to an Observable, then flattens all of these inner Observables using switch.
Source: ES6 Observables in RxJS
You don't want an API save data request to cancel. That is why you would use mergeMap. A search query can be thrown away, no loss of data, and the user might be editing their query and are not interested in the data for the old one. Hence switchMap.
Yes, if you are no longer concerned with the response of the previous request when a new Input arrives switchMap is a suitable operator than mergeMap.

How to cache the result of a Task when using it as an Observable with retry?

This is what I have:
CitiesObservable = Observable
.FromAsync(apiClient.GetCitiesTask)
.Retry();
apiClient.GetCitiesTask returns a task of type: Task<List<City>>
The problem is that every time I add a subscriber to the observable, apiClient.GetCitiesTask gets called again. How can I cache the result once it has completed successfully?
Thanks
Question reworded
I want apiClient.GetCitiesTask to be called as many times as needed (until it doesn't fail), but once it success, all late subscribers should use a cached result.
Conclusion
2 solutions arose, one I found and the other (the selected answer).
Solution A: (actually is almost a solution)
CitiesObservable = Observable.FromAsync(apiClient.GetCitiesTask).Publish();
CitiesObservable.Connect();
// Then you can subscribe as you want. But! you won't receive the cached value on late subscribers, only the onCompleted signal.
Solution B: (by #Bluesman)
CitiesObservable = Observable.StartAsync(
() => Observable.FromAsync(apiClient.GetPlacesTask<City>).Retry().ToTask()
);
// Then you can subscribe as you want.
What about....
Observable
.StartAsync(() => Observable
.FromAsync(reserbusAPI.GetPlacesTask<City>)
.Retry()
.ToTask());
The outer StartAsync makes sure the eventual result from the created task is buffered while the inner FromAsync with Retry makes sure that GetPlacesTask is called as many times as needed. However, the whole retrying-thing still starts even before the first subscription.

Resources