在复杂的承诺返回链中承诺catch()顺序

时间:2016-07-20 08:05:58

标签: javascript exception asynchronous promise redux

当我从函数A向函数B返回一个promise时,如何捕获错误,然后从A返回数据,并且两个调用都捕获了Promise?我知道,当承诺得到解决时,A然后总是首先被执行,接着是B'然后是从A&A返回的数据。但是,当Promises的这种回归形成一个长链时,我无法理解错误是如何被捕获的。以下是该场景的简化示例。我使用Redux-thunk动作创建器来管理状态。

function postActionCreator(data) {
  return (dispatch) => {
    dispatch(type: POST_LOADING)
    return Source.post()
      .then(response => {
        dispatch(type: POST_SUCCESS, payload: response)
        return response
      })
      .catch(error => {
        // Is this catch called if handlePost throws error in then?
        dispatch(type: POST_ERROR, payload: error)
        throw new Error(error)
      })
  }
}

// Container component's post function calling the Redux action creator
function handlePost(data) {
  this.props.postActionCreator(data)
    .then(response => {
      // Do something with response
    })
    .catch(error => {
      // Or is the error caught with this catch?
    })
}

// Or are the both catchs called, in which order and why?

在以下三种不同情况下如何处理错误:

  • Source.post抛出错误
  • postActionCreator然后抛出错误
  • handlePost'然后抛出错误

1 个答案:

答案 0 :(得分:5)

使用promises时,函数应该执行以下三种操作之一:

  1. 返回值
  2. 回复承诺
  3. 发生错误
  4. 对于这个问题,我们并不太关心前两种情况,但您可以在此处阅读promises resolution procedure以获取更多信息。那么让我们来看一下这个错误案例。

    在JavaScript中,错误 - 就像大多数事情一样 - 只是对象。创建错误并选择如何传播该错误是两回事。传播错误的两大类是同步和异步的。要同步传播错误,您必须throw它,对于异步,您只需通过某些预定义约定(例如回调或承诺)传递错误对象。

    要完全回答这个问题,我们需要了解如何处理这两种不同的错误类型。对于同步错误(已抛出),处理它们的唯一方法(除了捕获所有事件处理程序,如window.onerror)是将它们包装在try/catch语句中。对于异步错误,我们只遵循如何将这些数据传递回调用堆栈的约定。

    所以用这些知识回答你的问题:

    Source.post会抛出错误

    如果我假设“抛出错误”意味着“发生了错误”,我们无法在不知道Source.post的源代码的情况下知道这将如何表现。如果实际上发生了错误,那么可以说有一些意外的ReferenceError,那么它实际上根本不会被处理:

    function post() {
      foo.bar; // will throw
    }
    
    function run() {
      post()
        .then(log)
        .catch(log);
    }
    

    会导致:

    ReferenceError: foo is not defined
        at post (sync.js:6:3)
        at run (sync.js:10:3)
        at Object.<anonymous> (sync.js:15:1)
    

    现在,如果post函数实际异步处理错误,在这种情况下通过确认传递错误的promise约定,我们会发现它会被捕获:

    function post() {
      return new Promise((resolve, reject) => {
        reject('foo');
      });
    }
    
    function run() {
      post()
        .then(() => {})
        .catch((err) => {
          console.error('Caught error:', err);
        });
    }
    

    结果

    Caught error: foo

    另一个有趣的部分是,catch语句中的代码实际上抛出了一个新的Error对象。在这种情况下,我们最后要了解一件事。我们知道同步抛出错误意味着它必须被捕获,但是从then函数中抛出错误会导致被拒绝的异常,而不是错误,所以发生了什么?好吧,promise实现是在then块中内部包装传递给try/catch的函数,然后通过拒绝promise来处理此错误。我们可以这样证明:

    function post() {
      return new Promise((resolve, reject) => {
        resolve('foo');
      });
    }
    
    function run() {
      post()
        .then((result) => {
          throw result;
        })
        .catch((err) => {
          console.error('Caught error:', err);
        });
    }
    

    在这种情况下,也会发现错误。

    postActionCreator然后抛出错误

    现在变得简单了。捕获并传播then中的错误。它到达catch内的postActionCreator,然后重新向外catch

    handlePost然后抛出错误

    最简单的情况。它将在内部捕获,您将在catch后立即在then语句中收到错误。

    最后,您可能会想,“我如何处理Source.post中的同步错误?如果那不是我的功能怎么办?”。好问题!您可以使用Bluebird的promise.try等实用程序为您包装此功能。