How does the catch method get called when chaining HTTP promises?

Viewed 125

I have created a custom loaderService for my Angular application, that provides a loading spinner while a request (or a set of requests) is being handled by the back-end. Here is my code:

export class ComponentOne {
  constructor(loaderService: LoaderService, orderService: OrderService) {}
  ...
  ...
  method() {
    this.loaderService.display(true);
    this.orderService.firstRequest()
      // the misspelled word causes a 500 error
      .then((response) => this.orderService.methodWithWrongInput('misspelled_word'))
      // but it still executes the following then
      .then(() => this.method2())
      .catch(() => null)
      .then(() => this.loaderService.display(false));
  }
  ...
}

I display the spinner before the request, but I want to make sure I close it no matter if the request succeeds or fails. That's why I put a catch and final then at the bottom. The code works fine, but when I was debugging it I realized that even though the second request (with the intentionally misspelled word) fails, method2() is still being called. Or does it actually fail?

My question is, how does the catch method work when chaining requests. I thought if a request fails, we go straight to the catch method, but I might be wrong.

Edit: Turns out I was wrong about method2() being called. See T.J. Crowder's answer for more.

1 Answers

...I realized that even though the second request (with the intentionally misspelled word) fails, method2() is still being called...

I suspect you're misinterpreting what you're seeing in the debugger, which is easily done in this case.

In this code:

method() {
  this.loaderService.display(true);
  this.orderService.firstRequest()
    // the misspelled word causes a 500 error
    .then((response) => this.orderService.methodWithWrongInput('misspelled_word'))
    // but it still executes the following then
    .then(() => this.method2())
    .catch(() => null)
    .then(() => this.loaderService.display(false));
}

Here's what happens:

  1. this.loaderService.display(true); runs
  2. this.orderService.firstRequest() runs and returns a promise
  3. .then((response) => this.orderService.methodWithWrongInput('misspelled_word')) runs — not the part inside the callback, just the then. It registers a resolution handler on the promise and returns a new promise.
  4. .then(() => this.method2()) runs — again, just the then call, not its callback. It returns another new promise.
  5. .catch(() => null) runs — again, just the catch call, not its callback. It returns another new promise.
  6. .then(() => this.loaderService.display(false)) runs — again, just the then call, not its callback. It returns another new promise.

then later, the promise from firstRequest() resolves, and

  1. (response) => this.orderService.methodWithWrongInput('misspelled_word') runs and returns a new promise

then later, that promise rejects, and

  1. () => null (in your catch handler) runs and returns null, which resolves the associated promise (e.g., converts what was a rejection into a resolution)
  2. () => this.loaderService.display(false) runs, because the chain is resolved (not rejected) at this point (thanks to #8 above)

So in the debugger, you'll see the execution cursor step over the then call for method2, during the initial sequence above (#4) where the chain is being set up synchronously; but that's just adding the handler to the chain. You won't see method2 actually get called, because that callback is never triggered when the resolutions/rejections happen (in the case where methodWithWrongInput rejects)

Related