【问题标题】:Return success when handling error in a promise处理 promise 中的错误时返回成功
【发布时间】:2019-03-20 17:30:23
【问题描述】:

我有一个可以处理通过 Web API 执行的 HTTP 请求的承诺:

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 */ } );

在代码的后面,这个承诺被链接到更多的错误检查。

如果出现 404 错误,我实际上可以“修复”问题,并且我不希望其他处理程序触发。在这种情况下,我宁愿让诺言成功。我该怎么做?


更多代码来更深入地解释我的案例:

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.
    }

  });

}

【问题讨论】:

  • 您的promise 是如何声明的?您必须检测 404 错误并调用 reject()
  • 您能告诉我们您打算如何处理成功案例吗?
  • @Zenoo 这是vue-resource 返回的承诺。我无法控制声明
  • @AseemUpadhyay 请查看更新
  • 您是否尝试过使用interceptor

标签: javascript typescript vue.js promise


【解决方案1】:

您可以简单地返回拒绝/解决

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

我做了一个例子来展示它是如何工作的, 仅针对这种情况:

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')
})

【讨论】:

  • 真的不行(或者我不明白你的想法):jsfiddle.net/m0g23kzj
  • 我认为你没有正式使用promise,它不像流或可观察的,jsfiddle
  • 你完全正确,我在代码中犯了一个错误。 Jsfiddle 现在可以正常工作。稍后我会用我的代码对其进行测试,如果可行,请接受答案
  • 我制作了一个版本,以展示它在您的情况下是如何工作的。
【解决方案2】:

如果你使用standard Promise implementation,你可以简单地抛出一个错误,然后它将作为一个失败的promise结果返回给下一个catch方法:

// 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))

另一种方法是返回另一个您可以立即拒绝的承诺(通过返回new Promise((resolve, reject)=>reject('error message')) 或通过返回速记方法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))

第二个版本起作用的原因是因为来自thencatch 方法的任何返回值都被包装在一个promise 中并传递,因此您可以链接then 调用。 知道了这一点,如果你返回一个值,then 方法在幕后实际上会返回类似Promise.resolve(returnValue) 的东西,这样你就可以进行链接:

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

这就是为什么上面的sn-p和这个类似的原因:

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

考虑到这一点,您还可以返回一个被拒绝的 Promise,它实际上可以用 catch 方法捕获:

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

【讨论】:

    【解决方案3】:

    如果你需要在“修复”它时传播你的成功流程,我可以在这里想到两种方法,

    1. 您可以将进一步的传播逻辑包装在一个函数中,然后在错误场景中调用它。
    2. 将您的promise 包装在另一个promise 中并在错误404 上调用resolve(yourValue)。但这意味着您必须更改promise 对象上的进一步链接逻辑,但相反这会给您更灵活。

    希望对你有所帮助;)

    编辑

    看了你的代码后,我还是觉得按照我之前提到的方法是可以实现的。你可以做一些事情 -

    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();
               }
       })
    }
    

    【讨论】:

    • 不幸的是,这两个选项似乎都转向了更难阅读或支持的代码。我真的没想到用Promise 无法以简单的方式完成这样一个简单的案例。顺便说一句,请查看我对问题的更新 - 也许它会给你更多的见解
    • 虽然,我认为你已经标记了答案,这有帮助吗? @Archeg
    • 是的,谢谢,这很有帮助。我想我对Promise 的工作方式有错误的理解,所以我需要在这方面做更多的工作。如果您使用自定义 Promise 实现,您的方法应该会有所帮助,这是非标准的(许多库通常都是这种情况)。就我而言,我发现我使用了标准实现(我以为我不是,但我自己弄糊涂了)。无论如何,谢谢你的回复。包含所有答案的整个问题对我来说都是黄金,我在这里有很多有用的信息。
    猜你喜欢
    • 2017-08-09
    • 1970-01-01
    • 2014-06-29
    • 1970-01-01
    • 2019-02-17
    • 2020-03-15
    • 1970-01-01
    • 2019-01-05
    • 2016-04-20
    相关资源
    最近更新 更多