Angular.js服务/工厂具有可重用数据

时间:2014-09-27 05:03:51

标签: angularjs service factory

我正在尝试构建一个允许我通过某些API调用加载日期的服务或工厂。 大多数这些数据都需要重复使用,所以基本上我只想进行一次API调用,下次当我需要这些数据时,它应该只返回它。

现在无论何时我进行API调用,在它完成之前我都进行相同的调用,我希望第二次调用等到第一次调用完成。

基本上当我这样做时:

dataService.getMenu() // Make API call
dataService.getMenu() // Wait for the first API call to be completed and return that data

// Somewhere else
dataService.getMenu() // Return data as API call was already made

我的工厂看起来像这样:

(function() {
    var app = angular.module('dataService', []);
    app.factory('dataService', ['$http', '$q', function($http, $q) {
        var links = [],
            jobs = [];

        return {
            getMenu: function() {
                var deferred = $q.defer();

                console.log(links);

                if(links.length > 0) {
                    deferred.resolve(links);
                } else {
                    $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) {
                        links = data;

                        deferred.resolve(data);
                    })
                }

                return deferred.promise;
            }
        }
    }])
})();

3 个答案:

答案 0 :(得分:2)

只需将getMenu函数之外的defer声明移到工厂

即可
 app.factory('dataService', ['$http', '$q', function($http, $q) {
        var links = [],
            jobs = [],
            deferredMenu = $q.defer();

现在在getMenu调用中使用deferredMenu promise。

getMenu: function() {
                if(links.length > 0) {
                    deferredMenu.resolve(links);
                } else {
                    $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) {
                        links = data;

                        deferredMenu.resolve(data);
                    })
                }

                return deferredMenu.promise;
}

答案 1 :(得分:0)

您可以使用cache服务中的$http配置为您执行此操作。如 $http caching 文档中所述:

  

要启用缓存,请将请求配置缓存属性设置为   true(使用默认缓存)或自定义缓存对象(使用   $ cacheFactory)。启用缓存后,$ http将存储响应   来自指定缓存中的服务器。 下次同样的请求   生成后,响应将从缓存中提供,而不会发送   请求服务器

     

请注意,即使响应是从缓存提供的,也是如此   数据的异步方式与实际请求的方式相同。

     

如果对同一个URL有多个GET请求   使用相同的缓存进行缓存,但仅缓存尚未填充   将向服务器发出一个请求,其余请求将   使用第一个请求的响应来实现。

上述声明符合您问题中的要求。此外,我省略了$q服务,因为$http方法已经提供了您需要的承诺,您只需要使用then() $q服务在响应中包含数据对象方法

(function() {
    var app = angular.module('dataService', []);
    app.factory('dataService', ['$http', function($http) {
        return {
            getMenu: function() {
               return $http.get('../server/api.php?ajax=true&action=getCats', {cache: true})
                  .then(function(response) {
                    return response.data;
                  });
            }
        };
    }])
})();

答案 2 :(得分:0)

我知道我回答这个问题有点迟了,但我遇到了同样的问题,经过大量研究后提出了一个解决方案。

达到上述要求的方法是使用呼叫排队

以下是相同的步骤:

  1. 对于每次调用,创建一个promise并将promise添加到队列中。每次通话都会返回defer.promise
  2. 对于队列中的第一项,调用一个将带来api的函数,并在API的响应中设置参数IsDataPresent = true(最初为假)。
  3. 解析第一个调用的承诺,并在本地变量中设置接收的数据。执行队列中下一个调用的函数,但首先检查IsDataPresent=== true,如果为true,则使用局部变量数据解析下一个调用的promise。
  4. 请参阅以下代码:

    app.factory('dataService', ['$http', '$q', function($http, $q) {
        var links = '',
            jobs = [],
            isDataAlreadyPresent = false;
    
        var getMenuCall = function() { 
            var call = jobs[0]; // Retrieve first promise from the queue
            if (isDataAlreadyPresent) { // This will be false for first call and true for all other
                call.defer.resolve(links);
            } else {
                $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) { //Get API data
                    isDataAlreadyPresent = true; //Set parameter to true
                    links = data; // Set local variable to received data
                    call.defer.resolve.resolve(data); // Resolve the first promise
                    jobs.shift(); // Remove first item from the queue
                    if (jobs.length > 0) {
                        getMenuCall(); // Execute the function for next call's promise in the queue. This time isDataAlreadyPresent== true will be true so it's promise will be resolved by the links data thus avoiding extra call. 
                    }
                });
            }
    
            return deferredMenu.promise;
        };
    
        return {
            getMenu: function() {
                var defer = $q.defer(); // Create promise for the call
                jobs.push({
                    defer: defer // Push each call's promise to the queue
                });
                if (jobs.length === 1) { // For the first call make call above function which will make API call
                    getMenuCall();
                }
                return defer.promise; // Defer promise for the time being
            }
        }
    }]);