RxJs: finalize for not complete observable - rxjs

I have the following code:
this.userService.get().pipe(
catchError(() => EMPTY) // Do something here
).subscribe(() => {}) // And here
I want to do some stuff no matter there was an error or not. Just like finalize but for not completed observable. I tried to use tap, but it works only if there's no error. I don't want to duplicate my code and add it to the catchError. What should I do?

there's no other way to do it with catchError without touching it in the current example . Because everything after catchError won't get any notification, it's like an observable that never emits.
you can use 2nd argument of tap, it is triggered on error, the same as in subscribe.
this.userService.get().pipe(
tap(
() => console.log('emit'),
() => console.log('error'),
() => console.log('complete'),
),
// catchError(() => EMPTY), // closing the stream and hiding the error.
// repeat(), // in case if you want to resubscribe once the stream has been closed.
).subscribe(
() => console.log('emit'),
() => console.log('error'),
() => console.log('complete'),
);

Emit a default value in catchError.
this.userService.get().pipe(
catchError(() => of(null)) // catch error and emit null instead (or some other value)
).subscribe(value => {
// do something here,
// value will be null when this.userService.get() errors */
})
You should consider moving your error handling with catchError into the Service.

Related

Can I remove .subscribe() from map by using another RxJS operator?

Is there an alternative to doing .subscribe() in map?
return this.http.patch<any>(URL, { test: true }).pipe(
tap(_ => this.nextSomething$.next({})),
filter(_ => this.filter),
map(resp => {
this.someObservable({ message: 'do' }).subscribe()
return resp
})
)
I tried doing switchMap and returning the previous response but my observable does not complete.
switchMap(prevResp =>
this.someObservable({ message: 'do' }) }).pipe(map( _ => prevResp))
)
Thank you
If your observable is not completing when you switch to using the flattening operator then that means the target observable in that operator isn't completing. So you might want to check what's going on in someObservable that causes it to never complete. It is likey that behavior isn't desirable.
If you know that someObservable will emit at least once, then you can add the first operator to the inner pipe method:
switchMap(prevResp =>
this.someObservable({ message: 'do' }) }).pipe(
first(),
map( _ => prevResp)
)
)
If you don't care what someObservable does - you don't want to wait for it just want it to execute, then wrap the observable in firstValueFrom. It converts an observable into a promise that emits the first result. This code smells, but it should do the trick.
this.http.patch<any>(URL, { test: true }).pipe(
tap(() => this.nextSomething$.next({})),
filter(() => this.filter),
tap(() => firstValueFrom(this.someObservable({ message: 'do' })))
)
Maybe, you don't care to wait for this observable to emit, but you're still looking for an orderly execution of someObservable. In that case you can use a subject that emits every time you want it called, and use concatMap to ensure the execution is performed in an orderly fashion.
private readonly someObservableRequest = new Subject<string>();
constructor() {
this.someObservableRequest.pipe(
concatMap((message) => this.someObservable({ message }))
).subscribe(); // be kind, please unsubscribe
}
someMethod() {
return this.http.patch<any>(URL, { test: true }).pipe(
tap(_ => this.nextSomething$.next({})),
filter(_ => this.filter),
tap(() => this.someObservableRequest('do'))
);
}
If I understand your point right, you have to execute consecutively 2 Observables.
In this case you need yo use one so called "higher order" operators, i.e. either concatMap or switchMap (there are other "higher order" operators but I feel they do not apply to your case).
The code then would look like this
myNewOservable this.http.patch<any>(URL, { test: true }).pipe(
tap(_ => this.nextSomething$.next({})),
filter(_ => this.filter),
// an higher order operator returns an Observable
concatMap(resp => {
return this.someObservable({ message: 'do' })
})
)
return myNewOservable
Now you can subscribe to myNewOservable.
In my example I have used concatMap which ensures that a value notified by the upstream Observable is processed through the downstream pipeline before processing the next value from upstream.
I could have used also switchMap, which has a slightly different behavior: as soon as a new value is notified by the upstream Observable, any downstream processing is terminated.
In this case, given that http emits only one value and then completes the 2 operators act the same.

Reset Rx Js fromEvent observable after subscribe

I have mousemove event:
fromEvent(document, 'mousemove').pipe(
filter(e => e.target !== this.formElement.nativeElement),
filter(_ => this.form.valid),
take(1),
mergeMap((): any => this.confirm()),
).subscribe();
confirm() {
this.snack.open('sure', 'yes').onAction()
.subscribe(_ => {
this.createNoteService.create();
})
}
After confirming the snack button I want the mouse event to start working again.
Is it possible? Thanks!
The reason this only works for you the first time, is because you have take(1) which ends the observable after one value is received.
Also, your confirm() method should return observable; don't subscribe inside the function; mergeMap will automatically subscribe / unsubscribe for you.
However, instead of mergeMap, you can use exhaustMap. This will only allow a single inner subscription at a time. It will ignore any incoming emissions until its current source completes:
fromEvent(document, 'mousemove').pipe(
filter(e => e.target !== formElement.nativeElement),
filter(_ => form.valid),
exhaustMap(() => confirm()),
).subscribe();
function confirm() {
return snack.open('sure', 'yes').onAction().pipe(
tap(() => this.createNoteService.create())
);
}

Why is finalize lifted in 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:
What's the rationale behind this design decision? Can you give some explanation on why this is a desireable / advantageous property?
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?
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.
That's the whole principle of the finalize operator. To emit only -after- the source observable completes. Which does mean, after all the complete subscriptions have been handled, counting the tap complete. From the docs:
Returns an Observable that mirrors the source Observable, but will call a specified function when the source terminates on complete or error.
Now you could place the finalize in an inner observable, but I suppose you wouldn't like the order then either
of(null).pipe(
tap({ complete: () => console.log('tap 1 completes') }),
concatMap((resp) => of(resp).pipe(
finalize(() => console.log('finalize'))
)),
tap({ complete: () => console.log('tap 2 completes') })
).subscribe({ complete: () => console.log('subscribe completes') });
This will make the finalize execute before the first and last tap, this is because of the complete object you pass into tap. If you just pass in a function in the first tap, it will have the right order.
Another way could be the usage of concat:
concat(
of(null).pipe(
tap({complete: () => console.log('tap 1 completes' ) }),
finalize(() => console.log('finalize'))
),
EMPTY.pipe(
tap({complete: () => console.log('tap 2 completes' ) })
)
).subscribe({ complete: () => console.log('subscribe completes') });
But this kinda prevents you from accessing what ever the first observable emitted. So, basically, I don't think there is a proper solution to what you are asking :)

catch error and emit different action when handling ngrx-effects

In the code snippet below, I want to emit a GetData action if the update operation was successful or a BackendError action if unsuccessful.
#Effect()
updateData$ = this.actions$.pipe(
ofType(MyActionType.UPDATE_DATA),
map((action: UpdateData) => action.payload),
combineLatest(this.authService.getUser(), (myData, user) => this.dataService.updateData(myData, user)),
map(() => new GetData()),
catchError((err) => { of(new BackendError(err)))
);
The above code does not seem to be working. Even though the update operation fails due to permission error, the BackendError action is not emitted.
Any help is appreciated.
Your current implementation will swallow errors on the updateData operation, because the action$ (the outer observable) gets mapped to a new GetData() action, regardless of the result of the updateData operation (success or failure).
In the implementation below, the catchError operator will runif the updateData operation (dataUpdate$ -- the inner observable) throws an error..
#Effect()
updateData$ = this.actions$.pipe(
ofType(MyActionType.UPDATE_DATA),
mergeMap((action: UpdateData) => {
const user$ = this.authService.getUser();
const dataUpdate$ = user$.pipe(
mergeMap(user => this.dataService.updateData(action.payload, user));
);
return dataUpdate$.pipe(
map(() => new GetData()),
catchError(err => of(new BackendError(err)))
);
})
);
Additional Resources
Here's some more information on the difference between mergeMap
and map.
Official docs for mergeMap.
Example of #Effect using this approach in the #ngrx/effects README.

redux-observable epic that doesn't send any new actions

Might be that I'm a noob and not fully understanding how this stuff should work yet, but I have an epic in redux-observable in which I want to use as a way to create a promise which will dispatch an action and wait for a different action before resolving. I've got it working by mapping the action to '__IGNORE__' but I really don't want to do that. Is there any way to just have an epic handle an action, but not pass anything else on?
Here's my code:
export const waitFor = (type, action) => new Promise((resolve, reject) => {
const waitForResult = action$ => action$.ofType(type).do(() => resolve()).mapTo({type: "___IGNORE___"});
registerEpic(waitForResult);
action();
});
You can throw away any next'd values from an observable chain by using the .ignoreElements() RxJS operator
action$.ofType(type)
.do(() => resolve())
.ignoreElements();
Another way of doing this (no more right or wrong) is to create an anonymous Observable that just subscribes.
const waitForResultEpic = action$ => new Observable(observer =>
action$.ofType(type)
.subscribe(() => resolve())
);
This is implicitly returning the subscription we create, so that it's attached to the lifecycle of our rootEpic as well. Because we never call observer.next(), this epic never emits any values; just like ignoreElements().
Although you didn't ask, you may eventually notice that your epic will run forever, listening for that incoming action matching the type variable. This may not be not what you want, if you want to match once then complete.
You can accomplish that using .take(1) operator.
const waitForResult = action$ =>
action$.ofType(type)
.take(1)
.do(() => resolve())
.ignoreElements();
Or
const waitForResult = action$ => new Observable(observer =>
action$.ofType(type)
.take(1)
.subscribe({
next: () => resolve(),
error: err => observer.error(err),
complete: () => observer.complete()
})
);
This will only match once per the life of the application--once received it will never do it again.

Resources