2015-10-05 48 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 
相關問題