Archeg
Archeg

Reputation: 8462

Return success when handling error in a promise

I have a promise that handles a HTTP request performed over a Web API:

promise = promise.then(r => { 
    // ...
  }, error => {
    if (error.status == 404) {
      // Here I can fix an error and continue properly
    } else {
      // Here the error should be propagated further in the promise
    }
}

// later in the code:
promise.catch(r => { /* More error handling */ } );

Later in the code this promise is chained to more error checks.

In case of a 404 error, I can actually "fix" a problem, and I don't want the other handler to trigger. I'd rather want to make the promise a success in this case. How can I do that?


A bit more code to explain my case more deeply:

refresh() {
  this.refreshAsync().catch(r => {
     // Show error to the user.
     notifications.showError("Unexpected error happened");
  });
}

async refreshAsync() {
  // Here goes a lot of vue-resource calls to gather the necessary data for a view. They are chained with `await`. Only one of them:
  await this.$http.get(url).then(r => {
    this.data = r.data;
  }, error => {
    // 404 is actually a legit response for API to return, so the user notification above should not be shown
    if (error.status == 404) {
      // handle successfully
    } else {
      // propagate an error, so the handler above could show a warning to the user.
    }

  });

}

Upvotes: 9

Views: 4124

Answers (3)

Aseem Upadhyay
Aseem Upadhyay

Reputation: 4537

If you need to propagate your success flow when you "fix" it, I can think of two approaches here,

  1. You can wrap your further propagation logic in a function and then call it in the error scenario.
  2. Wrap your promise in another promise and call resolve(yourValue) on error 404. But then this will mean you'll have to change your further chained logics on the promise object, but on the contrary this will give you more flexibility.

Hope it helps ;)

EDIT

After seeing your code, I still feel it is achievable by what I have mentioned before. You can do something on the lines of -

refreshAsync = async () => {
   return new Promise((resolve,reject)=>{
       await this.$http.get(url).then(r => {
           this.data = r.data;
        }, error => {
            if (error.status == 404) {
                 resolve(); //populate this with your desired value
           } else {
                 reject();
           }
   })
}

Upvotes: 2

gion_13
gion_13

Reputation: 41533

If you're using the standard Promise implementation, you can simply just throw an error and then it will be returned as a failed promise result to the next catch method:

// let's assume we have the following async operation
const somePromiseCall = () =>
  new Promise(resolve => setTimeout(() => resolve(), 1000))

somePromiseCall()
  .then(() => {
    if (Math.random() > .5) {
      throw 'this is the error message'
    }

    return 'this is the result'
  })
  .then(result => console.log('success', result))
  .catch(err => console.error('fail', err))

Another way to do this is just by returning another promise that you can immediately reject (either by returning new Promise((resolve, reject)=>reject('error message')) or by returning the shorthand method Promise.reject('error message'):

// let's assume we have the following async operation
const somePromiseCall = () =>
  new Promise(resolve => setTimeout(() => resolve(), 1000))

somePromiseCall()
  .then(() => {
    if (Math.random() > .5) {
      return Promise.reject('error message')
    }

    return 'this is the result'
  })
  .then(result => console.log('success', result))
  .catch(err => console.error('fail', err))

The reason why the second version works is because any return value from a then or catch method is wrapped in a promise and passed on so you can chain then calls. Knowing this, if you return a value, the then method, behind the scenes, actually returns something like Promise.resolve(returnValue), so that you can do chaining:

somePromiseCall()
  .then(result => result + 1)
  .then(newResult => /* newResult === result + 1 */) 

That's why the above snippet is similar to this one:

somePromiseCall()
  .then(result => Promise.resolve(result + 1))
  .then(newResult => /* newResult === result + 1 */) 

Having this in mind, you can also return a rejected promise that actually can be caught with the catch method:

somePromiseCall()
  .then(result => Promise.reject('reason'))
  .catch(err => console.log('error', err)) 

Upvotes: 1

Timothy Lee
Timothy Lee

Reputation: 828

You can simply return a reject/resolve

if(error.status == 404)
    return Promise.resolve('OK')
else
    return Promise.reject('fail')

I made an example showing how this work, just for this case:

httpRequest = function () {
  return new Promise(function (res, rej) {
    let status = (Math.round(Math.random()) === 1) ? 200 : 404;
    console.log(status)
    if (status === 200)
        return res({ status })
    else
        return rej({ status })
  })
}

let promise =
httpRequest()
    .then(res => Promise.resolve('success'))
    .catch(e => {
        if (e.status === 404)
            return Promise.resolve('success')
        else
            return Promise.reject('failed')
    })

promise
.then(res => {
    console.log(res)
})
.catch(e => {
    console.log('this should not happen')
})

Upvotes: 3

Related Questions