【问题标题】:Calling only once / caching the data from a $http get in an AngularJS service在 AngularJS 服务中只调用一次/缓存来自 $http 的数据
【发布时间】:2014-07-26 01:58:50
【问题描述】:

这听起来像是一个非常简单/愚蠢的问题,但我需要问一下,因为我之前没有遇到过这种情况......好吧,我的 angularJS 应用程序中有一个服务。该服务目前包含 4 种方法,它们都执行 80% 相同的功能/代码,我希望提高效率。这是我的服务的样子(删除了很​​多代码):

    .factory('townDataService', function ($http) {

        var townList = {};

        townList.getTownList = function () {

            return $http({method: 'GET', url: '/api/country/cities'})
                .then(function (response) {

                    // HERE WE FORMAT THE response as desired... that creates a returnArray
                    var returnArray = [];
                    // loop through the countries
                    var JsonData = response.data;

                    for (key in JsonData['countries']) {
                         // formatting code...
                    }
                    // end of repeated CODE

                    return returnArray; // this is array, we don't do any formatting here

                });
        };


     townList.getCurrentTown = function (place) {

            return $http({method: 'GET', url: '/api/country/cities'})
                .then(function (response) {

                    // HERE WE FORMAT THE response as desired... that creates a returnArray
                    var returnArray = [];
                    // loop through the countries
                    var JsonData = response.data;

                    for (key in JsonData['countries']) {
                         // formatting code...
                    }
                    // end of repeated code

                    // now the format further / work with the returnArray...
                    for (var i = 0; i < returnArray.length; i++) {
                        // do stuff
                    }
                    return currentTown; // this is a string

                });
        };


        townList.getCurrentCountry = function (place) {

            return $http({method: 'GET', url: '/api/country/cities'})
                .then(function (response) {

                    // HERE WE FORMAT THE response as desired... that creates a returnArray
                    var returnArray = [];
                    // loop through the countries
                    var JsonData = response.data;

                    for (key in JsonData['countries']) {
                         // formatting code...
                    }
                    // end of repeated code

                    // now the format further / work with the returnArray...
                    for (var i = 0; i < returnArray.length; i++) {
                        // do stuff
                    }
                    return currentCountry; // this is a string

                });
        };

        return townList;

    }
)
;

现在我在每个方法中重复相同的$http 'GET' 和相同的格式化代码(这是很多嵌套循环),然后返回对象数组或字符串。这远非有效!将这个功能放入它自己的函数中的最佳方法是什么,所以我们只调用一次 GET url,但仍然用每个方法返回一个 promise?我是否应该将 $http({method: 'GET', url: '/api/country/cities'}) 的结果设置为 var 并在必要时在格式化数据之前将其注入/传递到每个方法中?我应该使用某种$cacheFactory吗?

对不起,如果这是一个愚蠢的问题,如果我没有很好地解释自己,我将重新提出问题。

提前致谢。

【问题讨论】:

    标签: javascript angularjs angularjs-service


    【解决方案1】:

    您没有什么特别的事情可以做并获得一些可观的好处。您肯定需要缓存 GET 响应并进行一些重构以避免代码重复并提高可读性:

    .factory('townDataService', function ($http) {
        var getCitiesAsync = function(){
            return $http({method: 'GET', url: '/api/country/cities', cache:true});
        };
    
        var townList = {};
    
        townList.getTownList = function () {
            return getCitiesAsync().then(prepareTownList);
        };
    
        var prepareTownList = function(response){
            //extract towns and do whatever you need
            return result;
        };
    
        ...
    

    至于使用 $cacheFactory - 对于这样一个简单的场景来说似乎是一种开销,只需使用内置缓存选项即可。

    【讨论】:

      【解决方案2】:

      正如你所说;此代码可以(并且应该)以多种方式重构。一个例子:

      让我们将 HTTP 内容分解为一个单独的服务,该服务也将负责缓存。 (另一个想法是为 HTTP/远程调用提供一个服务,另一个——也许是一个通用的装饰器——来处理缓存。让我们现在不讨论这么多细节。)让我们把格式化代码放在另一种方法:

      远程调用服务:

      .service('townHttpService', function($http, $q) {
          var cache;
      
          function getCities() {
              var d = $q.defer();
              if( cache ) {
                  d.resolve(cache);
              }
              else {
                  $http({method: 'GET', url: '/api/country/cities'}).then(
                      function success(response) {
                          cache = response.data;
                          d.resolve(cache);
                      },
                      function failure(reason) {
                          d.reject(reason);
                      }
                  });
              }
              return d.promise;
          }
      
          function clearCache() {
              cache = null;
          }
      
          return {
              getCities: getCities,
              clearCache: clearCache
          };
      })
      

      格式化程序:

      .service('townFormatter', function() {
          return function townFormatter(jsonData) {
              // HERE WE FORMAT THE response as desired... that creates a returnArray
              var returnArray = [], key;
              // loop through the countries
              for (key in jsonData['countries']) {
                   // formatting code...
              }
              // end of repeated CODE
              return returnArray; // this is array, we don't do any formatting here
          };
      })
      

      你的townDataService,写在上面:

      .factory('townDataService', function (townHttpService, townFormatter) {
      
          var townList = {};
      
          townList.getTownList = function () {
              return townHttpService.getCities().then(townFormatter);
          };
      
          townList.getCurrentTown = function (place) {
              return townHttpService.getCities().then(townFormatter).then(function(cityList) {
                  var currentTown;
                  for (var i = 0; i < cityList.length; i++) {
                      // do stuff
                  }
                  return currentTown; // this is a string
              });
          };
      
          townList.getCurrentCountry = function (place) {
              return townHttpService.getCities().then(townFormatter).then(function(cityList) {
                  var currentCountry;
                  for (var i = 0; i < cityList.length; i++) {
                      // do stuff
                  }
                  return currentCountry; // this is a string
              });
      
          return townList;
      })
      

      【讨论】:

      • 谢谢,这是一个很好的答案,但是使用您的方法,我确实收到以下错误: TypeError: Cannot read property 'then' of undefined (Object.townList.getSearchTown) and then undefined is not a function at在 WrappedCallback 成功 (a.url.com/scripts/service/TownHttpService.js:16:23) 我想我需要在 townDataService 中调用任何其他方法之前调用服务 townHttpService - 我认为该服务是空的?
      • 对不起,我认为 townHttpService.getCities().then(townFormatter) 没有向 .then(townFormatter) 返回任何内容
      • townHttpService.getCities() 似乎是问题,尽管它返回一个对象: Object {data: Object, status: 200, headers: function, config: Object, statusText: "OK"} 和Object.data 填充了从 GET 返回的内容?
      • getCities() 不应该返回这样的对象,如果实现如上。您确定不返回$http.get(...)
      • 啊好吧,让我检查一下...我显然实现了这个错误,我已经在这里上传了完整的代码(3个服务):plnkr.co/edit/fqzjioVuN38g5LZh21gg
      【解决方案3】:

      我猜你有两个问题,删除重复的逻辑和缓存结果的最佳方法。

      首先 - 删除重复代码: 貌似townList.getTownList是常用方法,其他两个方法是这个方法的扩展。 所以,

      townList.getCurrentTown = function(place) {
        var towns = townList.getTownList();
        for (var i = 0; i < returnArray.length; i++) { //additional stuff
        }
        return currentTown;
      };
      
      
      townList.getCurrentCountry = function(place) {
        var towns = townList.getTownList();
        for (var i = 0; i < returnArray.length; i++) { //additional stuff
        }
        return currentCountry;
      };
      

      第二个 - 缓存值 现在调用只在townList.getTownList进行http,缓存的逻辑可以在这里轻松实现。但这取决于数据是一次性获取还是可以刷新。 一次性获取:只需在http调用$http({method: 'GET', url: '/api/country/cities', cache:true});启用缓存

      根据请求刷新:我会传递一个refresh 变量来通知是否必须刷新数据。因此,如果刷新为真或 townList 为空,则将获取数据。

      var srvc = this;
      var townList;
      townList.getTownList = function(refresh ) {
        if (refresh || !townList) {
          srvc.townList = $http({
            method: 'GET',
            url: '/api/country/cities'
          })
            .then(function(response) {
              var returnArray = [];
              var JsonData = response.data;
              for (var key in JsonData.countries) {}
              return returnArray; // this is array, we don't do any formatting here
            });
        }
      
        return townList;
      };
      

      【讨论】:

        【解决方案4】:

        为了避免时间问题,稍微扩展一下解决方案可能会更好:

        function getCities() {
                var d = $q.defer();
                if( cache ) {
                    d.resolve(cache);
                }
                else {
                    $http({method: 'GET', url: '/api/country/cities'}).then(
                        function success(response) {
                            if (!cache) {
                                cache = response.data;
                            }
                            d.resolve(cache);
                        },
                        function failure(reason) {
                            d.reject(reason);
                        }
                    });
                }
                return d.promise;
            }
        

        在(可能是第二次或第三次)对 Web 服务的调用成功后,会检查是否在等待服务器响应时设置了缓存变量。如果是这样,我们可以返回已经分配的值。这样,如果发出多个调用,将不会对变量 cache 进行新的赋值:

        function success(response) {
            if (!cache) {
                cache = response.data;
        

        它不一定会产生问题,但是如果您依赖于拥有相同的对象(例如在使用数据绑定时),那么确保只获取一次数据是很棒的!

        【讨论】:

          猜你喜欢
          • 2018-01-28
          • 1970-01-01
          • 2012-12-16
          • 1970-01-01
          • 2014-04-29
          • 2017-03-04
          • 2016-05-19
          • 2015-12-03
          • 1970-01-01
          相关资源
          最近更新 更多