How can i retry after catch ?
I want to retry my observable after catch method automatically without calling another subscribe. How can i do that ?
I have something like this for now:
intercept(observable: Observable<Response>): Observable<Response> {
return observable.catch((err, source) => {
return this.refreshToken()
.flatMap(res => {
if (res.status === 200) {
return observable;
}
return Observable.throw(new Error('Can\'t refresh the token'));
});
});
}
So when i have my observable and call subscribe on it it will catch error -> refresh token -> and then return observable where i have to call another subscribe. I dont want to do that. how can i make it work with that subscribe before ?
The call example will looke something like this:
let request = this.http.request(url);
intercept(request).subscribe(res => { //do something });
There are a couple of operators which can be used in conjunction with catch to handle retrial/repetition. They are useful operators, they allow you to conditionally resubscribe to observables which have terminated. From the official documentation for retryWhen :
Repeats the source observable sequence on error when the notifier
emits a next value. If the source observable errors and the notifier
completes, it will complete the source sequence.
Additional info here :
retryWhen,
repeatWhen
You can find some examples here from previous questions:
Using `retryWhen` in http requests
How to retry only on certain error emitted by the source observable in RxJs
How to build an rx poller that waits some interval AFTER the previous ajax promise resolves?
From the second SO question :
src.retryWhen(function (errors) {
// retry for some errors, end the stream with an error for others
return errors.do(function (e) {
if (!canRetry(e)) {
throw e;
}
});
});
Related
I refactored my http layer to go from a promised-based implementation to observables using rxjs. The problem that I am facing is that the code crashes whenever server response is 400 or 500,
Axios.request(config).pipe(map(((response: AxiosResponse) => response), catchError(e => {
return new Observable(e);
})));
The problem I am facing is that the error is not being handled by the catchError callback. I am looking for a way in which the error is handled by the catchError callback so that the response can be handled gracefully.
I don't know your problem is this but you can do this:
import { EMPTY } from 'rxjs';
Axios.request(config).pipe(map(((response: AxiosResponse) => response),
catchError(() => EMPTY)));
EMPTY is an object that is imported from rxjs libaray.
It looks like you want to turn the error notification to next notification so you need to use return of(e) eventually EMPTY if you want to suppress the error.
new Observable(e) is probably what throws the error in your case because the parameter passed to Observable needs to be a function which e is not in this case.
This is more like question than resolve a problem.
I would like to know if there any scenario that both "Success" and Error" is not triggered.
The post call to "/logout" will result Http status return code 200
with empty respond body which is expected
import { httpClient } from angular/common/http;
private http: HttpClient;
this.http.post<any>('/logout', {})
.subscribe(
() => {
console.log("Logout");
}, error => {
console.log(error);
},
() => {
console.log("Finally");
});
It will output "Finally" 100% of time. That means success and error is not triggered at all.
Is there possibilities that either success and error not trigger. And clearly the http status code response is 200 OK.
Update:
The answer that #meriton provided work great.
Observable, in general, are not required to complete or error. They may remain live, and continue to emit values, forever.
However, Observable returned by HttpClient are guaranteed to terminate with either success or error (though the error may take a few minutes in case of a timeout) according to the HTTP status of the response. The presence of absence of a body does not affect this. If the request is successful, the observable will emit exactly one value: the response body (or null if the response body is absent).
I can not reproduce your claim that "success or error is not triggered at all". May you have misunderstood what the callbacks mean? When you provide three callbacks to subscribe, they are, in order:
the next callback, which receives emitted values
the error callback, which notifies that the Observable has aborted due to an error
the complete callback, which notifies that the Observable has completed successfully
The danger of mixing up callbacks is one reason why the RXJS team has deprecated passing several callbacks as separate arguments to subscribe in RXJS 8. The future proof way to write your code would be:
this.http.post<any>('/logout', {}).subscribe({
complete: () => {
console.log("Logout successful")
},
error: (error) => {
console.log(error);
}
});
BTW, none of these callbacks mean "finally", as in the finally clause of a try-statement, which is executed both in case of success and error. If you want to do something irrespective of whether the Observable completed successfully or failed with an error, you could use the finalize operator.
http library success depends on Status:200, it does not require message.body to be present
Example code of using RXJS pipe flow, where you can control the flow by capturing success & error, controlling timeout. It also demonstrates how you can use .subscribe() method as classic Finally
Example RXJS Flow:
this.http
.post<any>('/logout',{})
.pipe(
map(() => { // OK
return { success: true, err: null };
}),
timeout(10000), // CONTROL TIMEOUT
catchError((e) => { // IN CASE OF ERROR
return of({success: false, err:e});
})
)
.subscribe((result) => { // FINALLY here
if (result.success) {
console.log('Logged out successfully');
} else {
console.log('Logout failed', result.err);
}
});
I have an observable that submits the form submit$. This observable may end up with error with status code 403 what means that a user is not authorised and has to log in first.
Is there a way where I could on specific error code invoke another observable which performs an authorisation process. When authorisation is succeeded I want to repeat the submit$ without having user to invoke that observable once again.
To illustrate steps I want to have:
A user tries to submit and submit$ is being subscribed
This ends up with error with status code 403
Observable calls another authorise$ observable which has own workflow
When authorise$ succeeds the submit$ is invoked again
The process completes with success or error
If There is an error during authorise$ abort the submit$ process
I tried an approach here where I am separating into two observables, submit$ and authenticationSubmit$, and then I merge them again. I haven't tested it, and I am writing http.post(...) twice, so it is not exactly as you described it.
import { merge } from 'rxjs';
import { filter, switchMap } from 'rxjs/operators';
...
const submit$ = http.post(...);
const authenticationAndSubmit$ = submit$.pipe(
filter(httpResponse => httpResponse.status === 403),
switchMap(() => authService.login()),
filter(authResult => authResult === 'success'),
switchMap(() => http.post(...))
);
merge(submit$, authenticationAndSubmit$)
.pipe(
filter(httpResponse => httpResponse.status === 200),
)
.subscribe(httpResponse => {
// Do something
});
I'm trying to improve the use of promises in some code that retrieves REST resources. I have a number of REST calls that make the perform the same sequence of actions:
Fetch the config resource from the server if it hasn't been obtained previously
Dispatch a flux action indicating the start of the request
Send a request for the actual resource
Parse the JSON in the response
Dispatch a flux action indicating success with the parsed data.
The code I currently use to do this is below.
getThingsFromServer() {
return getConfigIfNeeded().then(() => {
dispatchStartOfRequestAction();
return window.fetch(`${store.baseURL}/resource`)
.then((response) => {
if(response.ok) {
return response.json();
} else {
return Promise.reject(new Error(`${response.status} ${response.statusText}`));
}
}, (error) => {
return Promise.reject(new Error(`Network error: ${error.message}`));
})
.then((data) => {
dispatchSuccessAction(data);
}, (error) => {
return Promise.reject(new Error(`JSON parse error: ${error.message}`));
})
.catch((error) => {
dispatchFailureAction(error)
});
});
}
There are a number of error conditions I would like to be able to handle individually, after which I want to dispatch a failure action (which is done in the catch()).
At the moment, if one of the individual then() error handlers gets called, every subsequent then() error handler is also called before finally calling the catch(). I want just one individual handler and the catch to be called.
I could dispense with handling each error individually and use a single catch at the end of it all but various sources both support and vilify the practice of handling all these different errors in the same way at the end of a promise chain. Is there a "right" answer to this beyond personal opinion?
if one of the individual then() error handlers gets called, every subsequent then() error handler is also called
Yes, if you throw (or return a rejected promise) from then handler, the promise will get rejected and subsequent error handlers will get called. There's really no way around this. To differentiate the errors, you'll have to nest (see also here).
In your case, you'll want to use
dispatchStartOfRequestAction();
return fetch(`${store.baseURL}/resource`)
.then(response => {
if (response.ok) {
return response.json()
.catch(error => {
throw new Error(`JSON parse error: ${error.message}`);
});
} else {
throw new Error(`${response.status} ${response.statusText}`);
}
}, error => {
throw new Error(`Network error: ${error.message}`);
})
.then(dispatchSuccessAction, dispatchFailureAction);
What is the best way to determine if the subscriber has finished executing or better yet return something and catch it up-stream? For example:
this._subscriptions.push(this._client
.getCommandStream(this._command) // Returns an IObservable from a Subject stream
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then(
// some more stuff
);
});
What's the best know to determine that subscription has finished. I've implemented it as follows:
this._subscriptions.push(this._client
.getCommandStream(this._command)
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then(re => {
// some more stuff
msg.done()
}).catch(err => msg.done(err));
});
i.e. added a done method to the object being passed in to determine if this is finished. The issue with that is I'll have to call done in every promise or catch block and find that a little too exhaustive. Is there a cleaner and more automated way of doing this?
I think the examples I've given are not good enough. This implementation is using RX to build an internal messaging bus. The get command stream is actually returning a read-only channel (as an Observable) to get commands and process them. Now the processing could be a http request followed by many other things or just an if statement.
this._client
.getCommandStream(this._command) // Returns an IObservable from a Subject stream
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then({
// some more stuff
}).then({
// Here I wanna do some file io
if(x) {
file.read('path', (content) => {
msg.reply(content);
msg.done();
});
} else {
// Or maybe not do a file io or maybe even do some image processing
msg.reply("pong");
msg.done()
}
});
});
I feel like this is a fine usage of the Observable pattern as this is exactly a sequence of commands coming in and this logic would like to act on them. The question is notice msg.done() being called all over the place. I want to know what is the best way to limit that call and know when the entire thing is done. Another option is to wrap it all in a Promise but then again what's the difference between resolve or msg.done()?
Actually, making another asynchronous request inside subscribe() isn't recommended because it just makes things more complicated and using Rx in this way doesn't help you make your code more understandable.
Since you need to make a request to a remote service that returns a PRomise you can merge it into the chain:
this._subscriptions.push(this._client
.getCommandStream(this._command)
.concatMap(msg => http.request(url))
.subscribe(...)
Also the 3rd parameter to subscribe is a callback that is called when the source Observable completes.
You can also add your own teardown logic when the chain is being disposed. This is called after the complete callback in subscribe(...) is called:
const subscription = this._subscriptions.push(this._client
...
.subscribe(...)
subscription.add(() => doWhatever())
Btw, this is equivalent to using the finally() operator.
As per RxJs subscribe method documentation, the last Argument is completed function
var source = Rx.Observable.range(0, 3)
var subscription = source.subscribe(
function (x) {
console.log('Next: %s', x);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
please refer this documentation
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/subscribe.md