I have a Subject (this.searchSubject) which I call next() on when I want to perform a search:
this.searchSubject.next(postBody)
This causes the following to fire:
this.searchSubject
.switchMap((view: any) => {
// upsert here records the search - needed
return this.searchHistoryService.upsert(view)
.flatMap(() => this.http.post(this.url, view))
.map((data) => this.pageTransform(data));
})
.subscribe(res => {
this.successSubject.next(this.pageTransform(res));
}, err => {
this.errorSub.next(err);
});
Unfortunately, no matter what I do, I can't seem to keep the stream alive if this.errorSub.next(err); is called (the error condition).
Since the httpClient (this.http.post) returns a new observable each I wouldnt have thought handling the error would be an issue, but it seems this removes all observers from this.searchSubject.
Note I have a httpInterceptor which returns a thrown error for every error returned.
This must be an extremely common pattern, so what am I doing wrong?
Your error handler is in the outer switchMap projection, thus it will close the outer stream in case of an error. You'll have to move it inside to your switchMap to keep the outer stream alive.
And since you're using rxjs#5.5.2 you can use the lettable operators which might make it easier to see where to put your error handlers.
this.searchSubject.pipe(
switchMap((view: any) => {
return this.searchHistoryService.upsert(view)
.pipe(
flatMap(() => this.http.post(this.url, view)),
map((data) => this.pageTransform(data)),
catchError(() => {
this.errorSub.next(err);
})
);
})
).subscribe(() => {
this.successSubject.next(this.pageTransform(res));
});
An important note if you switch to the lettable operators is that you have to import them from rxjs/operators like import { map, catchError } from 'rxjs/operators';
.
If you stay with the old syntax I think it will be as easy as to add a .catch after your .map() projection.
Related
I have parallel API calls. How to continue getting data from one of them if second one failed?
forkJoin([a,b])
.subscribe({
next: ((data) => {
const [first, second] = data;
})
It seems to me that combineLatest is what you are looking for, with a catchError to each of your observables to manage errors. Something like:
combineLatest([
obs1$.pipe(catchError(() => of(null)),
obs2$.pipe(catchError(() => of(null)),
]).subscribe(([first, second]) => { console.log(first,second); });
I have already found a solution, we need to handle errors in each observable and return of().
catchError(() => of([]))
I am trying to make 2 HTTP requests and in the first call I try to create a record and then according to its results (response from the API method) I want to execute or omit the second call that updates another data. However, although I can catch the error in catchError block, I cannot get the response in the switchMap method of the first call. So, what is wrong with this implementation according to teh given scenario? And how can I get the response of the first result and continue or not to the second call according to this first response?
let result;
let statusCode;
this.demoService.create(...).pipe(
catchError((err: any) => { ... }),
switchMap(response => {
// I am trying to get the response of first request at here
statusCode = response.statusCode;
if(...){
return this.demoService.update(...).pipe(
catchError((err: any) => { ... }),
map(response => {
return {
result: response
}
}
)
)}
}
))
.subscribe(result => console.log(result));
The question is still vague to me. I'll post a more generic answer to make few things clear
There are multiple things to note
When an observable emits an error notification, the observable is considered closed (unless triggered again) and none of the following operators that depend on next notifications will be triggered. If you wish to catch the error notifications inside the switchMap, you could return a next notification from the catchError. Something like catchError(error => of(error)) using RxJS of function. The notification would then be caught by the following switchMap.
You must return an observable from switchMap regardless of your condition. In this case if you do not wish to return anything when the condition fails, you could return RxJS NEVER. If you however wish to emit a message that could be caught by the subscriptions next callback, you could use RxJS of function. Replace return NEVER with return of('Some message that will be emitted to subscription's next callback');
import { of, NEVER } from 'rxjs';
import { switchMap, catchError, map } from 'rxjs/operators';
this.demoService.create(...).pipe(
catchError((err: any) => { ... }),
switchMap(response => {
statusCode = response.statusCode;
if (someCondition) {
return this.demoService.update(...).pipe( // emit `update()` when `someCondition` passes
catchError((err: any) => { ... }),
map(response => ({ result: response }))
);
}
// Show error message
return NEVER; // never emit when `someCondition` fails
}
)).subscribe({
next: result => console.log(result),
error: error => console.log(error)
});
You can implement with iif
this.demoService
.create(...)
.pipe(
// tap first to be sure there's actually a response to process through
tap(console.log),
// You can use any condition in your iif, "response.user.exists" is just a sample
// If the condition is true, it will run the run the update$ observable
// If not, it will run the default$
// NOTE: All of them must be an observable since you are inside the switchMap
switchMap(response =>
iif(() =>
response.user.exists,
this.demoService.update(response.id), // Pass ID
of('Default Random Message')
)
),
catchError((err: any) => { ... })
);
I'm trying to use the AngularFire library in an Angular application. Some of the AngularFire calls return promises, and I'd like to handle them as observables instead for consistency throughout the app. I'm using rxjs v6
Using from() works well and gives the expected behaviour except when errors occur.
If the promise throws an exception, the observable doesn't seem to see it and a stack trace gets dumped in the console saying Error: Uncaught (in promise).
My first attempt
The AngularFire call that returns the promise:
deleteCampaign(id: string) {
return from(this.campaignCollection.doc(id).delete());
}
The calling code:
deleteCampaign(id: string) {
return this.dataStorageService.deleteCampaign(id)
.pipe(
catchError(
err => {
console.log('error when deleting campaign');
console.log(err);
return throwError(err);
}
)
);
}
In this instance, I get the stack trace in the console and the catchError never fires.
My second attempt
I added a catch to the promise inside the from, and then tried rethrowing the error as an observable so it looked like this:
deleteCampaign(id: string) {
return from(this.campaignCollection.doc(id).delete().catch(
err => {
throwError(err);
}
));
}
My third attempt
Much like the second attempt, but I tried throwing a plain javascript error. This resulted in the same stack trace however, and it wasn't picked up by the observable either.
deleteCampaign(id: string) {
return from(this.campaignCollection.doc(id).delete().catch(
err => {
throw(err);
}
));
}
This stopped the stack trace happening, as now the promise was catching it, but the calling code still never sees the error.
Am I going about this the wrong way? I assumed that by using from() all of the error handling could occur in the observable, and I could leave the promise alone.
I need to be able to either:
1. Have no error handling code where the promise is returned and let the observable take care of it.
1. Have the promise catch block able to rethrow an error thats caught by the observable.
Here's the solution arrived at:
From the front end component, handle the passed error using the error callback in subscribe
onDelete(id: string) {
this.loadingCampaigns = true;
this.campaignService.deleteCampaign(id).subscribe(
_ => {},
err => {
console.log('error detection from the component');
}
);
}
From the campaign service, tap() the error so it can be logged or otherwise:
deleteCampaign(id: string) {
return this.dataStorageService.deleteCampaign(id)
.pipe(
tap(null, () => {console.log('tapped the error');} ),
);
}
Finally, from the data storage component do nothing at all:
deleteCampaign(id: string) {
return from(this.campaignCollection.doc(id).delete());
}
You can attach an error callback to Observable.subscribe().
Rx.Observable.from(Promise.reject('Boo!'))
.subscribe(val => {
console.log('success');
},
err => {
console.log(err);
});
// Boo!
deleteCampaign(id: string) {
return from(this.campaignCollection.doc(id).delete()).pipe(catchError(err=>{
return throwError(err);
}))
}
deleteCampaign(myid).susbcribe(res=>{
console.log(res);
},error=>{
console.log(error)
})
I put an example using ng-bootstrap modal -that return a promise when open the modal- to convert to a Observable in this stackblitz
This post is about better understand the React.component mechanism.
When trying to emit an event from my client to my server using socket, the communication works if I emit from outside any function or in the definition space above the render, in my React's component.
If I try to emit in a function or inside my promise's chain scope, it fails. I'm wondering why it works or fails since I have defined the elements in action, and my console.log of theses returns me their definition.
Here my snippet.js:
componentDidMount(){
console.log("on componentDidMount")
this.socket=io();
axios.get("https://api.ipify.org/?format=json")
.then((res) => {
// INSIDE THE PROMISE CHAIN'S SCOPE => FAILS TO EMIT ANYTHING
// I CAN'T FIGURE OUT WHY => BUT IT WORKS NOW.
// MAYBE A RESETTING I HAVE DONE WITHOUT HAVING SPOT THE CHANGEMENT
this.socket.emit("join", {ip:56565454654}, function(err){
});
})
// INSIDE A FUNCTION SCOPE IN componentDidMount() => FAILS TO EMIT ANYTHING WHEN CALL FROM PROMISE'CHAIN
// EDIT => WORKS => HAD TO BE BOUND
join(ip){
}
// OUTSIDE ANY FUNCTION SCOPE INSIDE componentDidMount() => EMIT SUCCEED BUT HARD TO HANDLE THE CODE FLOW SINCE IT ISN'T CALLED
this.socket.on("join",({message})=>{
})
}
// IN DEFINIITION AERA OUTSIDE componentDidMount() => EMIT SUCCEED AND CODE FLOW FRIENDLY
join(ip){
}
So why it works or fails?
So basically the following code works well:
A:
componentDidMount(){
console.log("on componentDidMount")
this.socket=io();
axios.get("https://api.ipify.org/?format=json")
.then((res) => {
console.log("ip adress: ", res.data.ip )
this.join(res.data.ip)
})
}
// IN DEFINIITION AERA OUTSIDE componentDidMount() => EMIT SUCCEED AND CODE FLOW FRIENDLY
join(ip){
console.log("this.socket.emit in join(): ", this.socket.emit)
this.socket.emit("join", {ip}, function(err){
if(err){
alert(err);
// Router.replace("/");
} else{
console.log("joinRoom succeed");
}
});
}
Any hint would be great,
thanks
Observable.interval(10000)
.switchMap(() => this.http.get(url))
.catch (err => Observable.empty())
.subscribe(data => render(data))
Each 10 seconds we make an HTTP call. If an error happens, observable becomes completed, it doesn't make any calls anymore. How to prevent that?
That's correct behavior, when a complete or error notification is sent observers unsubscribe and the chain is disposed.
You can use the retry() operator to resubscribe but it's hard to tell what is your goal from this brief description.
Observable.interval(10000)
.switchMap(() => this.http.get(url))
.retry()
.subscribe(data => render(data))
takeUntil() of observable.
RxJS implements the takeUntil operator. You can pass it either an Observable or a Promise that it will monitor for an item that triggers takeUntil to stop mirroring the source Observable.
for more info click here
Try this:
let dataX = Observable.interval(10000)
.switchMap(() => this.http.get(url));
let caught = dataX.catch(
Observable.return({
error: 'There was an error in http request'
}))
caught.subscribe((data) => { return render(data) },
// Because we catch errors now, `error` will not be executed
(error) => {console.log('error', error.message)}
)
if you want you can put any condition when the error comes like
if(!data[error]){
render(data)
}
I hope that it helps you
Observable.interval(10000)
.switchMap(() => this.http.get(url)
.map(res => res.json())
.catch (err => Observable.empty()))
.subscribe(data => render(data))