选项 1 - 中断/取消承诺链
HttpInterceptor 中的一个小改动可能会破坏/取消承诺链,这意味着控制器上的 activateOk 或 activateError 都不会被执行。
function HttpInterceptor($q, $location) {
var service = {
responseError: responseError
};
return service;
function responseError(rejection) {
if (rejection.status === 404) {
$location.path('/error');
return $q(function () { return null; })
}
return $q.reject(rejection);
}
}
return $q(function () { return null; }) 行,取消了承诺。
这是否“可以”是一个争论的话题。 “You don't know JS”中的凯尔·辛普森说:
许多 Promise 抽象库都提供了取消的功能
承诺,但这是一个糟糕的主意!许多开发者希望 Promise
本来就设计有外部取消功能,但是
问题是它会让 Promise 的一个消费者/观察者
影响其他消费者遵守相同 Promise 的能力。
这违反了未来价值的可信赖性(外部不变性),
更是“远距离行动”的体现
反模式...
好吗?坏的?正如我所说,这是一个有争议的话题。我喜欢它不需要更改任何现有的$http 消费者这一事实。
Kyle 说得很对:
许多 Promise 抽象库都提供了取消 Promise 的功能...
例如,Bluebird Promise 库支持取消。来自the documentation:
新的取消具有“无关”语义,而旧的
取消具有中止语义。取消承诺只是意味着
不会调用它的处理程序回调。
选项 2 - 不同的抽象
Promise 是一个相对宽泛的抽象。来自Promises/A+ specification:
promise 表示异步操作的最终结果。
Angular $http 服务使用承诺的$q 实现来为异步 HTTP 请求的最终结果返回一个承诺。
$http 有 two deprecated functions、.success 和 .error 是毫无价值的,它们装饰了返回的承诺。这些函数已被弃用,因为它们不能以典型的 Promise 方式链接,并且被认为没有作为“HTTP 特定”函数集增加太多价值。
但这并不是说我们不能制作我们自己的 HTTP 抽象/包装器,它甚至不暴露 $http 使用的底层承诺。像这样:
function HttpWrapper($http, $location) {
var service = {
get: function (getUrl, successCallback, errorCallback) {
$http.get(getUrl).then(function (response) {
successCallback(response);
}, function (rejection) {
if (rejection.status === 404) {
$location.path('/error');
} else {
errorCallback(rejection);
}
});
}
};
return service;
}
由于这不返回一个承诺,它的消费也需要有一点不同:
HttpWrapper.get('non-existent-location', getSuccess, getError);
function getSuccess(response) {
alert('Everything is ok');
}
function getError(error) {
alert('An error happened');
}
在 404 的情况下,位置更改为“错误”,并且不会执行 getSuccess 和 getError 回调。
此实现意味着链接 HTTP 请求的能力不再可用。这是一个可以接受的妥协吗?结果可能会有所不同...
选项 3 - 装饰拒绝
感谢 TJ 的评论:
如果您需要在特定控制器中进行错误处理,您将需要
检查是否已处理错误的条件
拦截器/服务等
HTTP 拦截器可以使用属性handled 来装饰拒绝承诺,以表明它是否处理了错误。
function HttpInterceptor($q, $location) {
var service = {
responseError: responseError
};
return service;
function responseError(rejection) {
if (rejection.status === 404) {
$location.path('/error');
rejection.handled = true;
}
return $q.reject(rejection);
}
}
控制器看起来像这样:
$http.get('non-existent-location')
.then(function activateOk(response) {
alert('Everything is ok');
})
.catch(function activateError(error) {
if (!error.handled) {
alert('An error happened');
}
});
总结
与选项 2 不同,选项 3 仍然为任何$http 消费者提供链接承诺的选项,这是一个积极的方面,因为它不会消除功能。
选项 2 和 3 的“远距离行动”都较少。在选项 2 的情况下,替代抽象清楚地表明事情的行为与通常的 $q 实现不同。对于选项 3,消费者仍会收到随心所欲的承诺。
所有 3 个选项都满足可维护性标准,因为更改全局错误处理程序以处理或多或少的场景不需要更改使用者。