Javascript AngularJS-承诺重新捕获异常
在以下代码中,$q promise的catch函数捕获异常:Javascript AngularJS-承诺重新捕获异常,javascript,angularjs,promise,angular-promise,Javascript,Angularjs,Promise,Angular Promise,在以下代码中,$q promise的catch函数捕获异常: // Fiddle - http://jsfiddle.net/EFpn8/6/ f1().then(function(data) { console.log("success 1: "+data) return f2(); }) .then(function(data) {console.log("success 2: "+data)}) .catch(function(dat
// Fiddle - http://jsfiddle.net/EFpn8/6/
f1().then(function(data) {
console.log("success 1: "+data)
return f2();
})
.then(function(data) {console.log("success 2: "+data)})
.catch(function(data) {console.log("error: "+data)});
function f1() {
var deferred = $q.defer();
// An exception thrown here is not caught in catch
// throw "err";
deferred.resolve("done f1");
return deferred.promise;
}
function f2() {
var deferred = $q.defer();
// An exception thrown here is handled properly
throw "err";
deferred.resolve("done f2");
return deferred.promise;
}
但是,当我查看控制台日志输出时,我看到以下内容:
该异常在Angular中捕获,但也被浏览器的错误处理捕获。这种行为确实在Q库中重现
是虫子吗?如何用$q真正捕获异常?Angular的
$q
使用一种约定,不管捕获到什么,都会记录抛出的错误。相反,如果要发出拒绝信号,则需要返回$q.reject(…
,如下所示:
function f2() {
var deferred = $q.defer();
// An exception thrown here is handled properly
return $q.reject(new Error("err"));//throw "err";
deferred.resolve("done f2");
return deferred.promise;
}
这是为了区分拒绝和SyntaxError之类的错误。就个人而言,这是一个我不同意的设计选择,但这是可以理解的,因为$q
很小,所以你不能真正构建一个可靠的未经处理的拒绝检测机制。在像Bluebird这样的强大库中,这类事情是不需要的
作为旁注,永远不要抛出字符串:这样会丢失堆栈跟踪。Angular的
$q
使用一种约定,在这种约定中,抛出的错误会被记录下来,而不管是否被捕获。相反,如果要发信号表示拒绝,则需要返回$q.reject(…
):
function f2() {
var deferred = $q.defer();
// An exception thrown here is handled properly
return $q.reject(new Error("err"));//throw "err";
deferred.resolve("done f2");
return deferred.promise;
}
这是为了区分拒绝和SyntaxError之类的错误。就个人而言,这是一个我不同意的设计选择,但这是可以理解的,因为$q
很小,所以你不能真正构建一个可靠的未经处理的拒绝检测机制。在像Bluebird这样的强大库中,这类事情是不需要的
作为旁注-永远不要抛出字符串:这样会错过堆栈跟踪。延迟是一种过时的、非常糟糕的构造承诺的方法,使用构造函数解决了这个问题,还有更多:
// This function is guaranteed to fulfill the promise contract
// of never throwing a synchronous exception, using deferreds manually
// this is virtually impossible to get right
function f1() {
return new Promise(function(resolve, reject) {
// code
});
}
我不知道您是否支持上述内容,如果不支持,您可以这样做:
function createPromise(fn) {
var d = $q.defer();
try {
fn(d.resolve.bind(d), d.reject.bind(d));
}
catch (e) {
d.reject(e);
}
return d.promise;
}
用法与承诺构造函数相同:
function f1() {
return createPromise(function(resolve, reject){
// code
});
}
延迟是一种过时的、非常糟糕的构建承诺的方法,使用构造函数解决了这个问题,还有更多:
// This function is guaranteed to fulfill the promise contract
// of never throwing a synchronous exception, using deferreds manually
// this is virtually impossible to get right
function f1() {
return new Promise(function(resolve, reject) {
// code
});
}
我不知道您是否支持上述内容,如果不支持,您可以这样做:
function createPromise(fn) {
var d = $q.defer();
try {
fn(d.resolve.bind(d), d.reject.bind(d));
}
catch (e) {
d.reject(e);
}
return d.promise;
}
用法与承诺构造函数相同:
function f1() {
return createPromise(function(resolve, reject){
// code
});
}
是虫子吗
否。查看会发现创建了一个故意的try/catch块来响应回调中抛出的异常
console.error
我怎样才能真正抓住$q的异常
回调将在一段时间后执行,此时当前调用堆栈已清除,因此您将无法在try
/catch
块中包装外部函数。但是,您有两个选项:
- 在回调中,在可能引发异常的代码周围放入
/try
块:catch
f1().then(function(data) { try { return f2(); } catch(e) { // Might want convert exception to rejected promise return $q.reject(e); } })
- 更改Angular的
服务的行为方式,如at。您可以将其更改为完全不做任何事情,这样控制台的错误日志中就不会有任何内容,但我不建议这样做$exceptionHandler
console.error
我怎样才能真正抓住$q的异常
回调将在一段时间后执行,此时当前调用堆栈已清除,因此您将无法在try
/catch
块中包装外部函数。但是,您有两个选项:
- 在回调中,在可能引发异常的代码周围放入
/try
块:catch
f1().then(function(data) { try { return f2(); } catch(e) { // Might want convert exception to rejected promise return $q.reject(e); } })
- 更改Angular的
服务的行为方式,如at。您可以将其更改为完全不做任何事情,这样控制台的错误日志中就不会有任何内容,但我不建议这样做$exceptionHandler
iit('test',inject(function($q, $timeout){
var finallyCalled = false;
var failValue;
var promise1 = $q.when(true)
.then(function(){
return $q(function(resolve,reject){
// Reject promise1
reject("failed");
});
})
.finally(function(){
// Always called...
finallyCalled = true;
// This will be ignored
return $q.when('passed');
});
var promise2 = $q.when(promise1)
.catch(function(value){
// Catch reject of promise1
failValue = value;
// Continue propagation as resolved
return value+1;
// Or continue propagation as rejected
//return $q.reject(value+2);
});
var updateFailValue = function(val){ failValue = val; };
$q.when(promise2)
.then( updateFailValue )
.catch(updateFailValue );
$timeout.flush();
expect( finallyCalled ).toBe(true);
expect( failValue ).toBe('failed1');
}));
下面是一个示例测试,它显示了新的$q构造函数、.finally()的使用、拒绝和承诺链传播:
iit('test',inject(function($q, $timeout){
var finallyCalled = false;
var failValue;
var promise1 = $q.when(true)
.then(function(){
return $q(function(resolve,reject){
// Reject promise1
reject("failed");
});
})
.finally(function(){
// Always called...
finallyCalled = true;
// This will be ignored
return $q.when('passed');
});
var promise2 = $q.when(promise1)
.catch(function(value){
// Catch reject of promise1
failValue = value;
// Continue propagation as resolved
return value+1;
// Or continue propagation as rejected
//return $q.reject(value+2);
});
var updateFailValue = function(val){ failValue = val; };
$q.when(promise2)
.then( updateFailValue )
.catch(updateFailValue );
$timeout.flush();
expect( finallyCalled ).toBe(true);
expect( failValue ).toBe('failed1');
}));
修正了AngularJS 1.6版
这种行为的理由是,未捕获的错误不同于常规的拒绝,正如
例如,它可能是由编程错误引起的。在实践中,这被证明是令人困惑的
或者对用户来说是不受欢迎的,因为本地promises和任何其他流行的promise库都不受欢迎
区分抛出的错误和常规拒绝。
(注意:虽然这种行为不违背承诺/A+规范,但也没有规定。)
$q:
由于,从承诺的onCompleted
或onRejection
处理程序引发的错误与常规拒绝处理程序的处理方式完全相同。以前,它还会传递给$exceptionHandler()
(除了以错误为理由拒绝承诺之外)
新的行为适用于所有人