捕获嵌套承诺中的所有块

Catch all block in nested promises

有没有办法在整个承诺链之后向 运行 添加一个 catch all 块?在下面的示例中,get 是一个通用的 lib 方法,它处理一些数据处理和 returns 到 callGet 的数据,后者获取数据并以任何方式处理 UI。我想在整个链运行s 之后创建一个捕获所有错误处理块运行s。我希望 catch all finally 块位于 get 块上,因为它是通用的 lib 方法。

// generic lib method used throughout codebase
function get(url, data) {
    return axios.get(url, data).then(res => {
        // handle response in whatever way (1)
    }).catch(error => {
        // handle error in whatever way (2)
    }).finally(() => {
        // RUN BLOCK AFTER callGet CHAIN (3)
    });
}

// specific call from somewhere within the codebase
function callGet() {
    get('/test', {}).then(res => {
        // do more with response from get (4)
    }).catch(err => {
        // handle error in UI somehow (5)
    });
}

callGet();

如果这是写出的单链,顺序将 运行 如下: (我知道这不是承诺的工作方式 - 只是写出想法的伪代码)

axios.get(url, data).then(res => {
    // (1) then in get block
}).then(res => {
    // (4) then in callGet block
}).catch(err => {
    // (2) catch in get block if errors in get block
}).catch(err => {
    // (5) catch in callGet block if errors in callGet block
}).finally(() => {
    // (3) run after callGet block
})

更新:

最终选择了 bluebird 的 onPossiblyUnhandledRejection http://bluebirdjs.com/docs/api/promise.onpossiblyunhandledrejection.html

它最终做了我需要它做的事情来捕捉开发人员没有专门处理的任何未处理的拒绝。

如果你想在后面的 catch 链中进一步处理它,只需在第一个 catch 块中重新抛出错误;

function get(url, data, onFulfilledHook, onRejectedHook) {
    return axios.get(url, data).then(res => {
        // handle response in whatever way (1)
    })
     .then(onFulfilledHook)
     .catch(error => {
        // handle error in whatever way (2)

        throw error; // rethrow the error <<<<<<<<<<<
    })
    .catch(onRejectedHook)
    .finally(() => {
        // RUN BLOCK AFTER callGet CHAIN (3)
    });
}

function callGet() {
    get('/test', {}, res => {
        // do more with response from get (4)
    }, err => {
        // handle error in UI somehow (5)
    });
}

你想要的不可能以你想要的方式实现。

原因很简单:.then的层级不同。

为了更容易理解,想象一下,它们都是同步的(不是,也不应该是;那只是一个思想实验)。没有 catchfinally,它看起来像这样:

function callGet(){
  try{
  // vvvvvvvvvvvv--- for sake of simplicity, assume that it's here (although it isn't)
    (function get(...){
      axios.get(...)
      // (1) then in get block
    })(...)
    // (4) then in callGet block
  }catch(err){
    // (5) catch in callGet block if errors in callGet block
  }
}

要添加 catchfinally,您必须将其更改为如下内容:

function callGet(){
  try{
    try{
      try{
      // vvvvvvvvvvvv--- for sake of simplicity, assume that it's here (although it isn't)
        (function get(...){
          axios.get(...)
          // (1) then in get block
        })(...)
        // (4) then in callGet block
      }catch(err){
        // (2) catch in get block if errors in get block
      }
    }catch(err){
      // (5) catch in callGet block if errors in callGet block
    }
  }finally{
    // (3) run after callGet block
  }
}

这很好,实际上,它会起作用,但是(不幸的是,一个“但是”)现在试试将 catch (2)finally (3) 移动到 get 函数 中。 显然没有办法这样做(不打乱他们的顺序)!

有了 promises, 会是一种非常棘手和复杂的方法来做类似的事情,但不要管它,让我解释一下为什么你不应该那样做。


每个函数都应该各司其职。最好是一份工作。您的 get 函数与 UI 等无关(至少不应该)。它的工作是发出网络请求,可能处理响应并处理错误。然而,它应该只处理它自己的错误,而不是它的调用者的

如果按照您想要的方式进行,get (2) 的 catch 也会捕获并处理 callGet (4),而 只留下 那个 catch 处理程序 (2)callGet 抛出的错误 catch (5).

这会使您的代码变得复杂、无语义、不可靠,因此难以维护。

您最好的办法是在 get 中自己处理错误,或者将错误处理留给调用者,不要试图破坏调用者的工作。如果您正在编写一个库,您可以为调用者提供辅助方法,以某种独特的方式组合承诺,但由调用者决定是否使用它。

如果你无论如何都想强制执行它,你可能会为此使用不同的编程模式(我不是问哪个的好人),JS 足够灵活,可以让你实现它。