您肯定错误地使用了promise机制,抛出用户定义的throw语句并不构成正确地将其捕获为promise拒绝。如$q 文档中所述:
当将延迟/承诺与熟悉的行为进行比较时
try/catch/throw,将 reject 视为 JavaScript 中的 throw 关键字。
这也意味着,如果您通过 promise 错误“捕获”错误
回调,并且您希望将错误转发到派生自的承诺
当前的承诺,你必须通过返回一个“重新抛出”错误
通过拒绝构造拒绝。
它们相似但不等价,要捕获用户定义的 throw 语句,您应该使用 catch 语句块。而$q 承诺应该只catch 拒绝承诺。因此,在您的情况下,返回被拒绝的承诺是处理流程而不是抛出用户定义的异常的正确方法。
DEMO
JAVASCRIPT
describe('Q Service Test', function() {
var $q,
$rootScope;
beforeEach(inject(function(_$q_, _$rootScope_) {
$q = _$q_;
$rootScope = _$rootScope_;
}));
it('Rejected promises are handled properly', function() {
var state = 'ok';
$q.when(1)
.then(function() {
return $q.reject('rejected');
})
.catch(function() {
state = 'error';
});
$rootScope.$digest();
expect(state).toBe('error');
});
});
更新:
您的代码在浏览器中以这种方式运行的原因是,Angular 的$q 实现在处理承诺队列时使用了try/catch 语句块。当任何回调抛出任何错误时,它会捕获错误本身,并以异常作为拒绝原因拒绝它,然后使用$exceptionHandler 记录错误。我建议你简单地返回被拒绝的承诺。
至于单元测试之所以会这样,是因为angular-mocks实现的$exceptionHandler与实际应用的$exceptionHandler不同。前者创建具有不同模式的提供程序,默认的 angular-mocks 实现使用rethrow 模式,该模式反过来抛出异常而不是记录它。如果您想让您的单元测试的行为方式与默认应用程序的$exceptionHandler 相同,那么您可以将模式设置为'log'。
DEMO
JAVASCRIPT
describe('Q Service Test', function() {
var $q,
$rootScope;
beforeEach(module('ng', function($exceptionHandlerProvider) {
$exceptionHandlerProvider.mode('log');
}));
beforeEach(inject(function(_$q_, _$rootScope_) {
$q = _$q_;
$rootScope = _$rootScope_;
}));
it('Caught exceptions are handled properly', function() {
var state = 'ok';
$q.when(1)
.then(function() {
throw new Error();
})
.catch(function() {
state = 'error';
});
$rootScope.$digest();
expect(state).toBe('error');
});
});