【发布时间】:2015-09-19 04:28:51
【问题描述】:
我有一个服务,它使用 $resource 为我获取项目类型列表。它对我来说效果很好,除了如果我进行多个几乎同时的调用(比如说,两个指令),每个调用都会创建另一个请求,而不是使用相同的 response/$promise/data。
我发现 this 将我引向 this 和 TL;DR,显然它正在创建一个多余的 $q.defer() 并且实际上被认为是 deferred anti-pattern。
如果获取项目类型的调用明显交错(例如相隔超过毫秒),则以下代码运行良好。使用 shared.projectTypes 解决连续调用。从某种意义上说,如果获取项目类型的请求失败,dfr.reject() 将被触发并被调用控制器中的.catch 捕获。
angular.module('projects')
.factory('projectService', function(notificationService){
// an object to share data gathered by this service
var shared = {};
// $resource for projects API
var projectResource = $resource(baseApiPath + 'projects', {}, {
...,
getProjectTypes: {
method: 'GET',
url: baseApiPath + 'projects/types'
},
...
});
// loads a list of project types
var loadProjectTypes = function(){
var dfr = $q.defer();
// if we've already done this, just return what we have.
if(shared.projectTypes){
dfr.resolve(shared.projectTypes);
}
else {
// begin anti-pattern (?)
projectResource.getProjectTypes(null,
function(response){
shared.projectTypes = response.result.projectTypes;
dfr.resolve(response);
},
function(errResponse){
console.error(errResponse);
notificationService.setNotification('error', errResponse.data.messages[0]);
dfr.reject(errResponse);
});
}
return dfr.promise;
};
return {
shared: shared,
project: projectResource,
loadProjectTypes: loadProjectTypes
};
});
所以,我读到没有必要拥有这个额外的var dfr = $q.defer(),因为 $resource 将为我提供所有这些。通过一些重构,我最终得到了这个:
...
// $resource for projects API
var projectResource = $resource(baseApiPath + 'projects', {}, {
...,
getProjectTypes: {
method: 'GET',
url: baseApiPath + 'projects/types',
isArray: true,
transformResponse: function(response){
return JSON.parse(response).result.projectTypes;
}
},
...
});
// loads a list of project types
var loadProjectTypes = function(){
return shared.projectTypes || (shared.projectTypes = projectResource.getProjectTypes());
};
...
为了澄清,我在资源中添加了isArray 和transformResponse,因为我的 API 返回了很多额外的元信息,而我想要的只是一个类型数组。在我的 loadProjectTypes 方法中,我包含了我们最初拥有的相同缓存,但我正在缓存 projectResource.getProjectTypes() 的结果而不是实际的响应数据(尽管这可能正是我正在缓存的内容,因为转换响应)。
这适用于愉快的路径(减少对 API 的调用,向每个人返回相同的东西等),但我的主要问题是错误的链接和捕获。
在我最初的反模式示例中,如果 GET /project/types 出现错误,我将使用 dfr.reject(),然后将其传递回我有一个 .catch() 的控制器。
这是来自控制器的代码,它实际上发出了获取项目类型的原始请求:
$q.all([
projectService.loadProjects(),
userService.loadUserRole('project_manager'),
userService.loadUserRole('sales_representative'),
projectService.loadProjectTypes(),
clientService.loadClients()
])
.then(function(response){
// doing stuff with response
})
.catch(function(errResponse){
// expecting errors from service to bubble through here
console.error(errResponse);
});
在反模式示例中,dfr.reject 导致错误显示在此处,但在我假设的非反模式示例中,它没有发生。我不确定如何以与以前相同的方式拒绝或解决 $resource 结果。如果 Promise 链接的要点之一是有一个点来处理来自任何链接链接的错误,那么我做对了。
我尝试使用 $q.resolve()/reject(),因为我不再有 dfr,但这似乎很愚蠢,无论如何也不起作用。
return shared.projectTypes || (shared.projectTypes = projectResource.getProjectTypes(null,
function(response){
return $q.resolve(response);
},
function(errResponse){
return $q.reject(errResponse);
}));
如何让链工作,以便控制器中的 .catch() 是处理错误的地方?
我是否真的在我的原始代码中实现了反模式,或者这是使用 $q.defer() 的公认方法之一,它根本不是反模式?
在second link I posted,有一个回答说:
“它有什么问题?但模式有效!你很幸运。 不幸的是,它可能不会,因为你可能忘记了一些优势 案子。在我见过的一半以上的事件中,作者都有 忘记处理错误处理程序。”
但是,我的原始代码 解决了这些错误。它正在工作,只是每个调用者都得到了自己的承诺。我觉得那是我错过了什么的地方。
我可能会感到困惑,但我认为loadProjectTypes 方法应该向调用它的任何人返回相同的承诺/数据,无论何时调用。它应该是任何 projectTypes 的真正来源,并且只进行一次调用,第一次。
任何时候我寻找这些(关于这些主题的大量紫色/访问过的谷歌链接),每个人要么显示链接与人为的例子,要么只使用 $http 或其他东西。我没有发现任何人在使用 $resource 的承诺链中进行错误捕获。
更新:添加我对解决方案的要求。我将它们发布在我的答案中,但也想将它们包含在原始帖子中。
要求 1:允许多次调用该方法,但只发出一个 API 请求,用相同的数据更新所有调用者。
要求 2:必须能够将方法的结果用作实际数据,正如 Promise 规范所期望的那样。 var myStuff = service.loadStuff() 实际上应该将 myStuff 设置为“东西”。
要求 3:必须允许 Promise 链接,以便链的任何部分中的所有错误都可以被链末尾的单个 catch 捕获。正如我在我的解决方案中发现的那样,可以有多个链和多个捕获,但关键是每个链都有一个捕获,并且链中任何断开的“链接”都应该将它们的错误报告给他们各自的收获。
【问题讨论】:
-
我在想也许我根本不应该使用额外的方法,控制器应该直接调用资源对象的方法: $q.all([..., projectService. project.getProjectTypes(), ...]).then(...);
-
这个答案暗示了我原来的“反模式”。现在真的很好奇我所做的是对还是错。 stackoverflow.com/a/25070789/2800116
-
是的,您肯定使用了延迟反模式。但是我看不到新代码在哪里失败,您确定
getProjectStatuses()返回的单个承诺被拒绝了吗? -
projectTypes和projectStatuses之间的区别是什么?它们是一样的,还是你在不同的方法上尝试不同的模式? -
@Bergi,projectTypes 就像“在线”或“移动”,状态是“开放”、“关闭”、“升级为销售”等。我试图让这个问题有点集中在只使用类型,除了我展示它是许多不同请求的一部分的实例。新代码本身并没有“失败”,但它并没有满足我的所有要求。
标签: javascript angularjs promise angular-resource