2015-10-05 67 views
6

我已经构建了一个通过$ http返回资源的复杂方法。

该方法返回一个承诺,然后检查我的本地缓存资源是否存在。如果是,它会返回缓存的资源,否则将发出$ http请求。这在资源被缓存后效果很好,但是我有多个函数在加载时触发这个方法的应用程序,并且它们中的每一个都会发出http请求,因为资源还没有被返回和缓存。

我想出了一个简单的检查来解决这个问题,但我觉得应该有更好的方法。我添加了一个布尔值,如果该方法处于获取资源的中间位置,则设置为true,如果是,则使用半秒超时解决该方法,以解决请求时间问题。代码如下。

那么,有没有更好的方法?

var schools = []; 
    var loadingSchools = false; 

    function getAllSchools(forceUpdate) { 
     return $q(function (resolve, reject) { 
      if(loadingSchools) resolve($timeout(getAllSchools, 500)); 

      else{ 

       loadingSchools = true; 

       if (schools.length && !forceUpdate) { 
        loadingSchools = false; 
        resolve(schools); 
        return; 
       } 

       console.log('$http: Getting All Schools - schoolService.js'); 

       $http.get(API_PATH + 'schools_GetAll', {cache:true}) 
       .success(function(result) { 
        schools = result; 
        loadingSchools = false; 
        resolve(schools); 
       }) 
       .error(function(error) { 
        schools = []; 
        loadingSchools = false; 
        reject(error); 
       }); 
      } 
     }); 
    } 
+1

也许创建管理等待承诺的,所以你基本上而不是设置超时您注册该功能,当最初的承诺将返回解析函数所有注册 – Saar

+2

的人都可以在路由器中使用更简单的解决方案,特别是使用'ui-router'时。你在用什么路由器? – charlietfl

+0

@Saar - 好想法,我正在考虑做那样的事情,但想先写这篇文章。我觉得有一种方法可以做到这一点,内置到$ http中,我可以利用它。 – Kolby

回答

20

首先,我不认为有必要将HttpPromise换成另一个承诺。使用success/error方法deprecated,您应完全依赖then()方法,并将HttpPromise视为常规承诺。

为了确保请求只发送一次,您可以实际跟踪您创建的第一个HttpPromise,并在随后的函数调用中返回相同的承诺。

这是一个将接受API端点作为参数的服务,并确保只有一个请求发送到该API。

app.factory('$httpOnce', [ '$http', '$cacheFactory', 
    function ($http, $cacheFactory) { 
    var cache = $cacheFactory('$httpOnce'); 

    return function $httpOnce(url, options) { 
     return cache.get(url) || cache.put(url, $http.get(url, options) 
     .then(function (response) { 
      return response.data; 
     })); 
    }; 
    } 
]); 

使用

function log(data) { 
    console.log(data); 
} 

// issues an HTTP request 
$httpOnce('https://api.github.com/').then(log); 
// does not issue an HTTP request, returns the same promise as above 
$httpOnce('https://api.github.com/').then(log); 

// ... 
// HTTP request completes somewhere, both promises above are resolved 
// ... 

setTimeout(function() { 
    // immediately resolved 
    $httpOnce('https://api.github.com/').then(log); 
}, 5000); 

这里是一个demo。您可以在开发工具中看到只发出一个请求。

+0

正是我在找的东西。感谢你的回答。 – Kolby

+0

我对这个解决方案有疑问,我想更好地理解它的工作原理。 当您将$ http.get结果放入缓存中时,您还需要在将其添加到缓存之前运行promise(以便$ http.get(url,options)返回一个承诺,然后使用它)。为什么你可以使用另一个。然后()获取缓存? 'cache.get'没有返回任何承诺。 $ http.get返回一个承诺并返回结果 - 返回另一个承诺或相同? 谢谢你的时间,让我知道如果你有任何参考资料让我阅读更多关于它。 – florinmtsc

+0

'cache:true'标志确实是一样的吗?我错过了什么吗? – zilj

0

我只是完全相同的问题,这里是我的服务

app = angular.module('MM_Graph') 

class Team_Data 
    constructor: ($routeParams,$rootScope, $cacheFactory, $q, API)-> 
    @.$routeParams = $routeParams 
    @.$rootScope = $rootScope 
    @.$cacheFactory = $cacheFactory 
    @.cache   = $cacheFactory('team_Data') 
    @.$q   = $q 
    @.deferred  = null 
    @.API   = API 
    @.project  = null 
    @.data   = null 
    @.team   = null 
    @.schema  = null 


    call_With_Cache: (method, params, callback)=>         # method that uses promises to prevent multiple parallel calls 

    cache_Key = "#{method}_#{JSON.stringify(params)}"       # create cache key using method and params 

    if not @.cache.get cache_Key            # if this is the first require for this type of data (method_project_team) 
     deferred = @.$q.defer()             # create a new instance of deferred 
     @.cache.put cache_Key, deferred.promise         # put its promise in the cache 

     on_Data_Received = (data)->            # simple callback method to resolve the promise 
     deferred.resolve(data)             # resolve promise (this is the data that will show as the 1st param in 'then') 

     method_Params = params.concat(on_Data_Received)       # append on_Data_Received callback method to the current 'method' params list 

     @.API[method].apply(null, method_Params)         # invoke 'method' in @.API 

    @.cache.get(cache_Key).then (data)->          # invoke the 'then' from the promise (will happen async until data is recevied) 
     callback (data)               # finally call the original callback with the data received from 'method' 

    clear_Data:()=> 
    @.cache.removeAll() 
    @.scores = null 
    @.schema = null 
    @.data  = null 
    @.deferred = null 

    load_Data: (callback)=> 
    if not (@.$routeParams.project and @.$routeParams.team)      # check that project and team are set 
     return callback() 

    if (@.$routeParams.project is @.project and @.$routeParams.team is @.team) # check if either the project or team have changed 
     # do nothing here since project or team have not changed 
    else 
     @.clear_Data()               # when project changes remove all cache entries 
     @.project = @.$routeParams.project 
     @.team = @.$routeParams.team 
     @.project_Schema (schema)=>            # get projecg schema 
     @.data_Score (scores)=>             # get current team scores 
      @.team_Get (data)=>             # get team data 
      @.scores = scores 
      @.schema = schema 
      @.data = data 

      @.deferred.resolve()            # trigger promise resolve 

    @.deferred ?= @.$q.defer()             # ensure @.deferred object exits 
    @.deferred.promise.then ->             # schedule execution 
     callback()                # invoke original caller callback 

    data_Score : (   callback) => @.call_With_Cache 'data_Score'  , [@.project, @.team  ], callback 
    project_Schema: (   callback) => @.call_With_Cache 'project_Schema' , [@.project    ], callback 
    radar_Fields : (   callback) => @.call_With_Cache 'data_Radar_Fields', [@.project    ], callback 
    radar_Team : (target_Team, callback) => @.call_With_Cache 'data_Radar_Team' , [@.project, target_Team ], callback 
    team_Get  : (   callback) => @.call_With_Cache 'team_Get'   , [@.project, @.team  ], callback 

    save: (callback)=> 
    @.API.file_Save @.project, @.team , @.data, callback 

app.service 'team_Data', ($routeParams, $rootScope, $cacheFactory, $q, API)=> 
    return new Team_Data $routeParams, $rootScope, $cacheFactory, $q, API 
相关问题