I am calling MS Graph to get a user's photo:
// lets get the photo itself
let photo$ = this.graph.get$(`users/${id}/photo/$value`, ResponseContentType.Blob)
.map(resp => {
return new Blob([resp.blob()], { type: resp.headers.get['Content-Type']}); })
.map(photo => {
let urlCreator = window.URL;
return this.sanitizer
.bypassSecurityTrustUrl(urlCreator.createObjectURL(photo));
})
.catch(function (e) {
if(e.status === 404) {
// no photo for this user!
console.log(`No photo for user ${id}! Returning default...`);
return undefined;
}
})
.toPromise();
However many users don't have a photo. In that case what I would like to do is create an object URL for some default photo (one of these perhaps) and emit that from the Observable. Or even just emit an undefined
from the Observable, which I can handle in the front end by displaying some default image.
I know that my catch is firing, because I see this in the browser console:
No photo for user xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx! Returning default... (output redacted)
I'm actually pulling in many different bits of data for each user, each in its own Observable. I then convert each of those Observable to Promises, and then do a Promise.all()
on all of them.
// let's pull all of this together to form a ConcreteUser
return Promise.all([photo$, ...])
.then(values => {
const photo = values[0];
...
The problem is in the case of a 404 for a user's photo, the whole Promise.all
aborts, and I can't retrieve the user.
I've tried catching errors in the photo$
observable and emitting an undefined
(which I planned to handle later), but it doesn't seem to be doing what I want.
How do I handle 404s in my Observable here, and emit something else (perhaps undefined
) instead of killing the observable?
I think I may have figured this out. A hint was lurking in the browser console...
TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
Reading the RxJs documentation on error handling showed them doing what I'm trying to do, so I was a bit confused by this. But then I noticed that in the documentation they call a mystery function getCachedVersion()
which I assumed returned an actual cached thing. However if they instead returned an Observable.of()
some actual cached thing, then that might explain it.
When I changed my catch code to:
.catch(function (e) {
if(e.status === 404) {
// no photo for this user!
console.log(`No photo for user ${id}! Returning default...`);
return Observable.of(undefined);
}
})
...it all started working.
To translate Johns answer into an up-to-date rxjs implementation, this now looks like:
import { Observable, throwError, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
const observale: Observable<unknown>;
observale.pipe(
catchError(err => {
if(err.error.statusCode === 404) {
return of(undefined);
} else {
//important to use the rxjs error here not the standard one
return throwError(err);
}
})
)
.subscribe(val => {
console.log('success but may be undefined');
}, err => {
console.log('error');
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With