I've got an Api call that is converted to a promise. My handleError function inside the observable re-throws via throwError. This re-thrown error does not trigger any catch in the outer Promise chain.
callApi() {
return this.http.get(`${this.baseUrl}/someapi`)
.pipe(
map((data: any) => this.extractData(data)),
catchError(error => this.handleError(error))
).toPromise();
handleError(error) {
console.error(error);
return throwError(error || 'Server error');
}
Calling code...
this.someService.callApi()
.then((response) => {
// THIS GETS CALLED AFTER throwError
// do something cool with response
this.someVar = response;
})
.catch((error) => {
// WE NEVER GET TO HERE, even when I force my api to throw an error
console.log(`Custom error message here. error = ${error.message}`);
this.displayErrorGettingToken();
});
Why doesn't the throwError trigger the Promise catch?
You should not use toPromise() when possible.
Use subscribe instead of then.
Also when you catch the error in a pipe it won't be thrown in then because you already caught it, also when you throw the error in a catch error, it won't be emitted into the regular pipe flow of your response.
callApi() {
return this.http.get(`${this.baseUrl}/someapi`);
}
This is totally ok. Http.get() returns a singleton observable stream, which emits only ONE value and then completes. Subscribe to the Observable.
this.someService.callApi()
.subscribe((response) => {
// THIS GETS CALLED always wenn everything is ok
this.someVar = response;
},
(error:HttpErrorResponse) =>{
console.log(`Custom error message here. error ${error.message}`);
this.displayErrorGettingToken();
});
Observable is like an extended version of promise. Use it.
Related
I have used a async function to wait for the settlement of promise and store the result in a variable. But it is not storing incase the promise is rejected. It is stroing result when promise is resolve but not when promise is rejecte. Any help will be highly appreciated.
async function umar() {
let somevar = false;
let myPromise = new Promise((resolve, reject) => {
if (somevar === true) {
console.log('I am resolved');
resolve('resolved')
}
else {
console.log('I am rejected.')
reject('rejected')
}
})
let myprom = await myPromise
console.log(myprom)
return myprom
}
let a = umar()
a.then((value)=>{
console.log(value)
},()=>{console.log('I got an error.')})
I was expecting the await to store the value in variable in both cases(when promise is rejected and resolve) but it is not doing so incase promise is rejected.
This is how await works. When the promise is rejected, it throws and you have to catch the error locally with a try/catch if you want to see the error locally. Your assignment statement let myprom = await myPromise will not be executed when myPromise rejects.
Promise rejections have to be caught in a .catch(), with the second callback to .then(fn1, fn2) or, if using await with a try/catch around the await (or sometimes you let the rejection propagate out of the async function and the caller can then catch the rejection.
In your umar() function, when myPromise rejects, the function will throw at the point of await myPromise and the promise that the async parent function returned will then reject with the same error that myPromise rejected with (as the async function automatically catches the throw and turns it into a rejection or its promise). You can either wrap a try/catch around that await to catch the error or in your code example, you can catch the error where you call umar() which you are already doing. If you log the actual error there, you would see the error you expect:
let a = umar();
a.then((value) => {
console.log(value)
}, (err) => {
// log the error
console.log('I got an error.', err);
});
When dealing with concatMap, how can I abort the execution of observables further down the line and prevent calling the completion handler?
Here is a simple example.
of(...[1, 2, 3]).pipe(
concatMap(t => of(t)),
map(n => {
console.log(n);
if (n === 2) {
throw new Error('OK, fail here');
}
}),
catchError((e, c) => of(console.log('Caught ' + e)))
)
.subscribe(
{
complete: () => console.log('Complete should not be triggered in an error case, but here it is'),
error: (err: any) => {
console.log('I did never trigger, thats ok!');
}
});
A source observable emits 1,2,3 which is piped into concatMap.
There are two possible scenarios: All three observables are emitted without error in this case complete handler should be triggered. Or the other case depicted here: Somewhere down the line there is an error like when n === 2. ConcatMap stops executing the next observable which is perfect but it still triggers the completion handler, which is undesired.
Actual result
1
2
Caught Error: OK, fail here
Complete should not be triggered in an error case, but here it is
Desired result
1
2
Caught Error: OK, fail here
Any hints? My previous attempt was to throw in the next handler but that turned out to be really bad :/ as it triggered a hostReportError
Ok,
since I was able to answer all the other fun questions regarding RxJS by myself, here we go :)
Remember: catchError replaces the faulted observable by some other observable. If catchError would therefore return an observable via throwError it would halt the execution and even better, it would trigger the error handler that was idle before.
of(...[1, 2, 3]).pipe(
concatMap(t => of(t)),
map(n => {
console.log(n);
if (n === 2) {
throw new Error('OK, fail here');
}
}),
catchError((e, c) => throwError(e)) // This line has changed
)
.subscribe(
{
complete: () => console.log('Complete should not be triggered in an error case, but here it is'),
error: (err: any) => {
console.log('Now I have something to do, yay!');
}
});
I want to get a value from a promise and then to return this value from the function. I'm using Axios to get the userId from the DB. I read that I should use .then but it still keeps returning a promise pending:
export async function getCurrentUserName(user){
const userName = await http.get(`${apiUrl}/users/me`,user)
.then(user =>{ return user.data.name });
return userName;
}
log:
userName PromiseĀ {pending}
_proto: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "avi"
The only way to get a value from a promise is with .then() or with await.
You cannot directly return the value from your function because your function returns BEFORE the asynchronous value is ready. So, your function needs to return the promise and the caller then must use .then() or await to retrieve the value from the promise.
In the code you show in your first code block, an async function ALWAYS returns a promise so you can't change that. Whatever value you return within the async function becomes the resolved value of the promise that was already returned.
To help you understand, let's review the timing of events in your function:
export async function getCurrentUserName(user){
const userName = await http.get(`${apiUrl}/users/me`,user)
.then(user =>{ return user.data.name });
return userName;
}
getCurrentUserName() gets called.
The function calls http.get()
The interpreter sees the await and suspends execution of the async function at the point and waits for the promise returned by http.get() to resolve/reject.
The function then returns an unresolved promise and execution in the code after the call to getCurrentUserName() continues. That calling code receives the unresolved promise. There is no way to return the value here because the value isn't yet known. That's why the promise is returned as it's a placeholder for the future value.
Sometime later, the http.get() resolves its promise and the execution of your function continues. It executes the .then() handler to get user.data.name.
The interpreter gets to the line return userName;. Since this is an async function that has already actually returned a promise, this return statement triggers that previously resolved promise to now get resolved with the resolved value set to userName.
A caller that was using await or .then() on the previously returned promise will get notified that the promise has now resolved and they will receive the userName resolved value.
This will solve your problem.
export async function getCurrentUserName(user) {
let userName = await http.get(`${apiUrl}/users/me`,user)
.then(user => {
if (user) {
username = user.data.name
} else {
alert('Failed to data')
}
return userName;
}
Or you can use a try and catch block.
async getCurrentUserName(user) {
try {
let response = await http.get(`${apiUrl}/users/me`,user);
let user = await response.json();
} catch(err) {
// catches errors both in fetch and response.json
alert(err);
}
}
Or So whenever you send data always use post rather than get
async getCurrentUserName(user) => {
await http.post(`${apiUrl}/users/me`,user)
.then(user =>{ return user.data.name })
.catch(function (error) {
return error;
})()
}
A "authenticationService" provides the following authenticate method. I'm unable to enter the piped catchError. What am I missing?
authenticate(credentials: { username: string; password: string }){
return new Observable<any>((observer: Observer<any>) => {
// ... calling the service, obtaining a promise
const authenticationPromise = ... ;
// This is a promise, it must be converted to an Observable
authenticationPromise
.then(() => {
observer.next('ok');
observer.complete();
})
.catch(err => {
console.log('service error ' + err);
throw new Error('crap');
});
});
}
Setting all the ngrx & ngrx/effect part aside, this authentication method is called upon user request:
(redux stuff).(({ payload }: actions.LoginRequestAction) =>
context.authService.authenticate(payload)
.pipe(
map(() => new GenericSuccessAction(...))
// Even though a throw statement happened in the authenticate method, this is never reached:
catchError((err: Error) => {
console.log('[debug] error caught: ', err);
return of(new actions.LoginFailureAction());
}),
)
)
As stated here, catchError is used to:
Gracefully handle errors in an observable sequence.
First of all, you are basically handling the error in your promise by catching the error in your promise. Throwing the error in the promise doesn't return an observable that emits an error.
You can:
Convert your promise into an observable and don't use .catch at all.
Return the error as an observable with rxjs' throwError(err)
Either way, the way you create your observable is questionable.
This is a much better and concise way to handle promises in rxjs:
from(authenticationPromise)
.pipe(
map(() => 'ok')
)
I have a confusion about switchMap in rxjs Observable:
for example i have next code:
Observable.fromEvent(button, 'click')
.switchMap(() => Observable.fromPromise(fetch('http://return-error.com')))
.subscribe(
(response) => {
console.log(response);
},
(error) => {
console.log(error);
}
);
If I get error from fetch, subscription is interrupted. So is there way to handle it to not create new subscription for any error?
I have even tried to catch error and return Observable, but subscription is interrupted anyway.
upd: how to deal with angular 2 http.get instead of fetch?
It's always more helpful if you make a Bin when asking these questions. I did it for you on this one.
You simply need to swallow the error in the fetch call. E.g.
fetch('bad-url').catch(err => 'Error but keep going')
Here's the demo. Click the document (output) to fire the event.
http://jsbin.com/vavugakere/edit?js,console,output
(You'll need a browser with native Fetch implementation or it'll throw an error)