0

I have function called request:

function request (endpoint) {
  return axios.request(endpoint).then(api.onSuccess).catch(api.onError)
}

api.onSuccess:

  onSuccess (response) {
    let breakChain = false
    ... some logic goes here ...
    return breakChain ? (new Promise(() => {})) : response
  }

api.onError:

  onError (error) {
    let breakChain = false
    ... some logic goes here ...
    if (breakChain) {
      return new Promise(() => {})
    } else {
      throw error
    }
  }

api holds a lot of functions that represent different API calls based on provided endpoints data and return request(endpoint).

Currenly I have code, as you can see, that return Promise with empty executor that is always in pending state to stop the chain of subsequent .then(...) and .catch(...) handlers from execution as they just infinitely wait for that Promise to settle. This is done to handle certain API responses that have common response handling (like responses with code >= 500). The problem is that now I need a call to .finally() (like in Vue cookbook - https://v2.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html#Dealing-with-Errors) to restore some component's state nevertheless of whether there is an error or not, but this approach of pending Promise is an obstacle.

The question is: is it possible to skip all subsequent .then(...) and .catch(...) calls within one of such handlers to go directly to .finally()?

Update. I haven't mentioned the important bit - api.onSuccess and api.onError are basic handlers. In another application components there are additional handlers appended to the end of that basic chain presented in request function. Usual chain of some API call has a following resulting form:

return axios.request(endpoint).then(api.onSuccess).catch(api.onError).then((response) => {...}).catch(() => {...}).finally(() => {...})

(sometimes there is no .finally() or .catch(...) block)

tony19
  • 125,647
  • 18
  • 229
  • 307
Roman Yakubovich
  • 893
  • 1
  • 7
  • 18
  • What purpose do `api.onSuccess` and `api.onError` serve? From here it looks like all they do is make it difficult to use `request` properly. – T.J. Crowder Apr 23 '19 at 17:53
  • @T.J.Crowder they are initial handlers - i.e. `api.onError` checks whether response has code >= 500 and if it does, set `breakChain` flag to `true` as this scenario (code >= 500) has common handling across the application – Roman Yakubovich Apr 23 '19 at 17:58
  • @RomanYakubovich you can always write your own `Promise`-like and implement all the fancy things. (Not even need to compliant with standard.) – apple apple Apr 23 '19 at 18:35

2 Answers2

2

Is it possible to skip all subsequent .then(...) and .catch(...) calls within one of such handlers to go directly to .finally()?

No.

Currenly I stop the chain by just infinitely waiting - yet this approach of pending Promise is an obstacle.

Indeed, don't do that. You can skip then handlers by using rejections (exceptions) for flow control, but the more appropriate way is to handle this by nesting the part of the chain to be skipped inside an if statement.

This is done to handle certain API responses that have common response handling (like responses with code >= 500)

For that, you should use something like

return axios.request(endpoint).then(response => {
    …
}).catch(error => {
    if (api.handleCommonError(error)) return; // returns false if it couldn't handle the error
    …
}).finally(() => {
    …
});

Yes, you cannot hide this kind of error handling inside an api.request function.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

You can use async and await. All modern browsers support them, and your bundler can make them compatible with older browsers.

For example:

async function request (endpoint) {
  try {
    const response = await axios.request(endpoint);
    return api.onSuccess(response);
  } catch (err) {
    api.onError(err);
  } finally {
    // Always executed, even if no error was thrown
  }
}

You can also do it more traditionally:

function request (endpoint) {
  return axios.request(endpoint).then(api.onSuccess, api.onError).then(() => {
    // Code is always executed after error / success
  }
}
coyotte508
  • 9,175
  • 6
  • 44
  • 63
  • I think I understand what you mean, but I can't use this way. Please look at the question update (sorry not to include that initially) – Roman Yakubovich Apr 23 '19 at 18:08
  • No, `async`/`await` doesn't help here. The `finally` block still doesn't run if an `await`ed promise never resolves. – Bergi Apr 23 '19 at 18:14
  • If an awaited promise doesn't resolve nothing helps, neither the `then` nor the `catch` would happen. Using `async` / `await` at least lets you branch off and do conditional logic far more easily than with promises. – coyotte508 Apr 23 '19 at 20:04