Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angularjs/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/378.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在承诺链中使用$resource(修复延迟反模式)_Javascript_Angularjs_Promise_Angular Resource - Fatal编程技术网

Javascript 在承诺链中使用$resource(修复延迟反模式)

Javascript 在承诺链中使用$resource(修复延迟反模式),javascript,angularjs,promise,angular-resource,Javascript,Angularjs,Promise,Angular Resource,我有一个服务,其方法使用$resource获取项目类型列表。它对我来说工作得很好,除了如果我进行多个几乎同时进行的调用(比如说,两个指令),每个调用将创建另一个请求,而不是使用相同的响应/$promise/数据 我找到了引导我去和TL;DR,显然它创建了一个冗余的$q.defer(),实际上被认为是一个 如果获取项目类型的调用明显错开(比如间隔超过毫秒),那么下面的代码工作得很好。连续调用使用shared.projectTypes解析。如果获取项目类型的请求失败,则调用控制器中的.catch将触

我有一个服务,其方法使用$resource获取项目类型列表。它对我来说工作得很好,除了如果我进行多个几乎同时进行的调用(比如说,两个指令),每个调用将创建另一个请求,而不是使用相同的响应/$promise/数据

我找到了引导我去和TL;DR,显然它创建了一个冗余的$q.defer(),实际上被认为是一个

如果获取项目类型的调用明显错开(比如间隔超过毫秒),那么下面的代码工作得很好。连续调用使用shared.projectTypes解析。如果获取项目类型的请求失败,则调用控制器中的
.catch
将触发
dfr.reject()
,并将其捕获

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()
的结果,而不是实际的响应数据(尽管由于transformResponse,这可能正是我缓存的结果)

这是一条很好的途径(减少对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
导致错误显示在catch中,但在我假定的非反模式示例中,它没有发生。我不知道如何以与以前相同的方式拒绝或解决$resource结果。如果承诺链接的一个要点是有一个点来处理来自任何链接的错误,那么我做得对

我尝试使用$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()的公认方法之一,而它根本不是反模式

在这本书中,有一个答案是:

“这有什么问题吗?但是这个图案很有效!你真幸运。”。 不幸的是,它可能没有,因为你可能忘记了一些优势 在我所看到的一半以上的事件中,作者 忘记处理错误处理程序。“

然而,我的原始代码正在解决这些错误。除了每个打电话的人都得到了自己的承诺之外,这一切都在起作用。我觉得我错过了一些东西

我可能会感到困惑,但我认为
loadProjectTypes
方法应该向调用它的任何人返回相同的承诺/数据,无论何时调用它。它应该是所有项目类型的真正来源,并且只在第一次调用时调用一次

每当我查找这些内容时(这些主题上有很多紫色/访问过的谷歌链接),每个人都会用人为的例子显示链接,或者只使用$http或其他东西。我还没有发现任何人在使用$resource的承诺链中执行错误捕获

更新:为解决方案添加我的需求。我把它们贴在了我的答案里,但我也想把它们也包括在原来的帖子里

要求1:允许对方法进行多个调用,但只发出一个API请求,用相同的数据更新所有调用方

要求2:必须能够使用方法的结果作为实际数据,正如承诺规范的意图
var myStuff=service.loadStuff()
实际上应该将
myStuff
设置为“stuff”


要求3:必须允许promise链接,以便链的任何部分中的所有错误都可以被链末端的单个捕捉器捕捉。正如我在解决方案中发现的,可以有多条链和多个捕获,但关键是每条链都有一个捕获,链中任何断开的“链接”都应该将其错误报告给各自的捕获。

我不久前写了一篇与此非常类似的文章,但有几个关键区别:

  • 我只在数据已经在缓存中时创建承诺,并在实际请求启动时返回本机承诺

  • 我添加了资源请求已挂起时的第三种状态

  • 代码的简化版本如下所示:

    module.factory("templateService", function ($templateCache, $q, $http) {
        var requests = {};
        return {
            getTemplate: function getTemplate(key, url) {
                var data = $templateCache.get(key);
                // if data already in cache, create a promise to deliver the data
                if (data) {
                    var deferred = $q.defer();
                    var promise = deferred.promise;
                    deferred.resolve({ data: data });
                    return promise;
                }
                // else if there is an open request for the resource, return the existing promise
                else if (requests[url]) {
                    return requests[url];
                }
                // else initiate a new request
                else {
                    var req = $http.get(url);
                    requests[url] = req;
                    req.success(function (data) {
                        delete requests[url];
                        $templateCache.put(key, data);
                    });
                    return req;
                }
            },
        };
    });
    

    这不是一直都是这样吗,只要你一说出你的问题,你就会找到你的解决方案

    要求1:每个方法调用只发出一个请求。这可以通过反模式的原始修复来解决。这将始终通过返回缓存的$resource或同时返回和缓存来返回$resource结果

    var loadProjectTypes = function(){
        return shared.projectTypes || (shared.projectTypes = projectResource.getProjectTypes());
    };
    
    要求2:能够使用服务方法作为承诺,在这里我可以将$scope变量的值直接设置为loadProjectTypes()的结果。使用上面修改过的方法,我可以简单地声明
    $scope.theTypes=projectService.loadProjectTypes()
    ,当类型出现时,它将自动填充类型列表,就像promise规范所希望的那样

    要求3:能够将多个$resource调用链接在一起,并让它们的错误被单个
    .catch()
    捕获。通过使用re的$promise
    $q.all([
        ...,
        projectService.loadProjectTypes().$promise,
        ...
    ])
    .then(function(response){
        // my project types comes in as response[n]
    })
    .catch(function(errResponse){
        // but any errors will be caught here
    });
    
    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',
                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());
        };
    
        return {
            shared: shared,
            project: projectResource,
            loadProjectTypes: loadProjectTypes
        };
    });
    
    
    angular.module('projects')
    .directive('projectPageHeader', ['projectService', function(projectService){
        return {
            restrict: 'E',
            scope: {
                active: '@',
            },
            templateUrl: 'src/js/apps/projects/partials/dir_projectPageHeader.html',
            replace: true,
            controller: function($scope){
                $scope.projectService = projectService;
    
                // sets the types to the array of types
                // as given by the transformResponse
                $scope.types = projectService.getProjectTypes();
    
                // could also do a .$promise.catch here if I wanted.
                // all catches will fire if get projectTypes fails.
            }
        };
    }]);
    
    angular.module('projects')
    .controller('projectListPageController', [
        '$scope','projectService',
    function($scope, projectService){
    
        // load it all up
        $q.all([
            projectService.loadProjectDetails($routeParams.projectId).$promise,
            userService.loadUserRole('project_manager').$promise,
            userService.loadUserRole('sales_representative').$promise,
            projectService.loadProjectStatuses().$promise,
            projectService.loadProjectTypes().$promise,
            clientService.loadClients().$promise
        ])
        .then(function(response){
            // do work with any/all the responses
        })
        .catch(function(errResponse){
            // catches any errors from any of the $promises above.
        })
    }]);
    
    // This code is just placed in your controllers init section
    loadProjectTypes()
    .$promise
    .then(function(response){
        // ... do something with response (or noop)
    })
    .catch(function(errResponse){
        // ... do something with error
    });
    
    return shared.projectTypes || (shared.projectTypes = projectResource.getProjectTypes());
    
    // again, this code is just placed somewhere near the top of your controller
    $q.all([
        loadProjectTypes().$promise
    ])
    .then(...)
    .catch(...);