Global Error handling with Observable.toPromise()

Viewed 4688

Hi i am using angular 5 and i am writing a global handler for the same which looks like following.

@Injectable()
export class ErrorsHandler implements ErrorHandler {
  constructor(
    private injector: Injector,
  ) { }


  handleError(error: Error | HttpErrorResponse) {
    const router = this.injector.get(Router);
    const zone = this.injector.get(NgZone);
    console.log('Here')
    console.log(error)

    if (error instanceof HttpErrorResponse) {

      // Client Error Happend
      zone.run(() => router.navigate(['/error'], { queryParams: { error: JSON.stringify(error) } }))

    } else {
      // Log the error anyway
      router.navigate(['/error'], { queryParams: { error: JSON.stringify({ message: 'Failed' }) } });
    }
  }
}

Everything works fine in Observable world ie if i do a failed http call like following

fireServerError() {
    this.httpService
            .get('https://jsonplaceholder.typicode.com/1')
            .subscribe(data => console.log('Data: ', data));
  }

and if the server call fails i get an error object properly as shown in the console image

enter image description here

But instead of that if i change it to a promise using toPromise(), like following

fireServerError() {
    this.httpService
            .get('https://jsonplaceholder.typicode.com/1')
            .toPromise();
  }

i get the following string stack trace instead of error Object itself

enter image description here

What am i doing wrong. How to throw/get the error object in case of unhandled promise rejections. Please help. I am stuck;

Please find the stackblitz link Here

1 Answers

Once you use .toPromise(), you are essentially putting the behavior into a different execution context (read ECMAScript 10.4), which means that errors must be handled in/around the new execution context rather than having them bubble up in Angular like you are expecting.

I can't say I entirely follow your example code (is fireServerError always supposed to throw an error via an HTTP call?), but it seems like you want to try to execute promises without local error handling, but rather to have any error from a promise bubble up to the angular error handling. I'm not sure I'd recommend that, I believe it's best-practice to handle promise errors locally (i.e. using Promise.prototype.catch or a try/await/catch block when you create the promise).

That being said, error handling is of course a complicated topic and if you are dead set on just handling all errors at the global level then you can try using a global window event handler to catch all unhandled promise rejections and handle them there:

window.addEventListener("unhandledrejection", event => {
  event.preventDefault(); // prevent the default promise rejection behavior
  console.error(event); // do whatever you want with the error
}, false);

The MDN guide on promises may also help clear some things up, hope that helps!

Related