如果我从函数A向函数B返回一个promise,从A返回数据,并且两个调用都捕获了Promise,那么错误如何被捕获?我知道,当承诺得到解决时,A然后总是首先被执行,然后是B,然后是从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然后抛出一个错误
Will Demaine.. 5
使用promises时,函数应该执行以下三种操作之一:
返回一个值
回报承诺
抛出一个错误
对于这个问题,我们并不太关心前两种情况,但您可以在此处阅读有关承诺解决程序的更多信息.那么让我们来看一下这个错误案例.
在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.(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
函数内抛出错误会导致被拒绝的异常,而不是错误,所以发生了什么?那么,承诺实现内部包裹你传递给函数then
在一个try/catch
块,然后通过拒绝承诺处理这个错误.我们可以证明这样:
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
捕获并传播a中的错误.它到达catch
内部postActionCreator
然后被重新投入到外面catch
.
handlePost然后抛出一个错误
最简单的情况.它会被内部捕获,你会在catch
声明后立即得到错误then
.
最后,你可能会想,"我怎么能处理那些同步错误Source.post
?如果那不是我的功能怎么办?".好问题!您可以使用Bluebird的promise.try之类的实用程序为您包装此函数.
使用promises时,函数应该执行以下三种操作之一:
返回一个值
回报承诺
抛出一个错误
对于这个问题,我们并不太关心前两种情况,但您可以在此处阅读有关承诺解决程序的更多信息.那么让我们来看一下这个错误案例.
在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.(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
函数内抛出错误会导致被拒绝的异常,而不是错误,所以发生了什么?那么,承诺实现内部包裹你传递给函数then
在一个try/catch
块,然后通过拒绝承诺处理这个错误.我们可以证明这样:
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
捕获并传播a中的错误.它到达catch
内部postActionCreator
然后被重新投入到外面catch
.
handlePost然后抛出一个错误
最简单的情况.它会被内部捕获,你会在catch
声明后立即得到错误then
.
最后,你可能会想,"我怎么能处理那些同步错误Source.post
?如果那不是我的功能怎么办?".好问题!您可以使用Bluebird的promise.try之类的实用程序为您包装此函数.