【问题标题】:Angular.js service/factory with reusable data具有可重用数据的 Angular.js 服务/工厂
【发布时间】:2014-09-27 05:03:50
【问题描述】:

我正在尝试构建一个允许我通过一些 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;
            }
        }
    }])
})();

【问题讨论】:

    标签: angularjs service factory


    【解决方案1】:

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

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

    现在在 getMenu 调用中使用 deferredMenu 承诺。

    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;
    }
    

    【讨论】:

    • 我也是这么想的,但是如果我快速调用getMenu两次,它会执行两次$http.get(),是否只是添加一个额外的变量来查看是否已拨打电话?
    • 我可能解释错了,基本上在加载$http.get()时会有几秒钟的时间,每当我在此期间拨打getMenu()时,我都不想打同样的电话再次。
    • 是的,使用这种方法第二次通话不会进行。请记住,该函数始终返回相同的延迟对象。第一次调用该函数会创建 defer 对象,所有其他的都会得到相同的 defer。
    • 谢谢,有道理!
    【解决方案2】:

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

    要启用缓存,请将请求配置缓存属性设置为 true(使用默认缓存)或自定义缓存对象(使用 $缓存工厂)。启用缓存后,$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;
                      });
                }
            };
        }])
    })();
    

    【讨论】:

    • 谢谢,我之前看过缓存中的构建,我在这里不这样做的原因是我可能需要修改返回数据,我想在我的服务中保留这个逻辑。
    • 您仍然可以在.then() 方法中修改数据。
    【解决方案3】:

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

    实现上述要求的方法是使用呼叫排队

    以下是相同的步骤:

    1. 为每个调用创建一个promise 并将promise 添加到队列中。为每个呼叫返回 defer.promise
    2. 对于队列中的第一项,调用一个函数,该函数将带入您的 api,并在 API 的响应中设置一个参数,例如 IsDataPresent = true(最初为 false)。
    3. 解决第一次调用的承诺并将接收到的数据设置在局部变量中。执行队列中下一次调用的函数,但首先检查IsDataPresent=== true,如果为true,则使用局部变量的数据解析下一次调用的promise。

    请参见下面的代码:

    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
            }
        }
    }]);
    

    【讨论】:

      猜你喜欢
      • 2020-06-03
      • 1970-01-01
      • 2018-05-12
      • 1970-01-01
      • 1970-01-01
      • 2013-11-27
      • 1970-01-01
      • 2014-03-12
      • 1970-01-01
      相关资源
      最近更新 更多