2015-10-05 6 views
6

Ho creato un metodo un po 'complesso per restituire una risorsa tramite $ http.

Il metodo restituisce una promessa e quindi controlla la cache locale se le risorse esistono ancora. Se lo fa, restituirà le risorse memorizzate nella cache, altrimenti effettuerà la richiesta $ http. Funziona alla grande dopo che la risorsa è stata memorizzata nella cache, ma ho più funzioni tramite l'applicazione che sta premendo questo metodo al caricamento e ognuna di esse eseguirà la richiesta http perché le risorse non sono state ancora restituite e memorizzate nella cache.

Mi è venuto in mente un semplice controllo che corregge questo, ma sento che dovrebbe esserci un modo migliore. Ho aggiunto un valore booleano impostato su true se il metodo è nel mezzo di ottenere la risorsa e, in tal caso, risolvo il metodo con un timeout di mezzo secondo, per fornire il tempo di richiesta alla risoluzione. Il codice è sotto

Quindi, c'è un modo migliore?

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

forse creare una funzione che gestisce l'attesa promette in modo che, fondamentalmente, invece di impostare il timeout si registra a tale funzione e quando la promessa originale ritorna si risolve tutti quelli che hanno registrato – Saar

+2

di gran lunga più semplici da usare nel router, specialmente se si utilizza 'ui-router'. Quale router stai usando? – charlietfl

+0

@Saar - Pensavo bene, stavo considerando di fare qualcosa del genere, ma volevo fare questo post prima. Ho pensato che ci sarebbe stato un metodo per farlo incorporato in $ http che potevo sfruttare. – Kolby

risposta

20

Prima di tutto, non credo che sia necessario avvolgere il HttpPromise in un'altra promessa. Con i metodi success/errordeprecated, è necessario fare affidamento esclusivamente sul metodo then() e considerare lo HttpPromise come una normale promessa.

Per assicurarsi che la richiesta venga inviata una sola volta, è possibile tenere traccia del primo HttpPromise creato e nelle chiamate successive della funzione, restituire la stessa promessa.

Questo è un servizio che accetta un endpoint API come argomento e garantisce che solo una richiesta venga inviata a tale 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; 
     })); 
    }; 
    } 
]); 

Uso

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

Ecco un demo. Puoi vedere negli strumenti di sviluppo che è stata emessa una sola richiesta.

+0

Esattamente quello che stavo cercando. Grazie per la risposta. – Kolby

+0

Ho una domanda riguardante questa soluzione, voglio capire meglio perché funzioni. Quando si inserisce il risultato $ http.get nella cache, si esegue anche la promessa (quindi $ http.get (url, opzioni) restituisce una promessa e la si utilizza) prima di aggiungerla alla cache). Perché puoi usarne un altro.allora() quando si ottiene la cache? Non ci sono promesse restituite da 'cache.get'. $ http.get restituisce una promessa e restituisce il risultato - restituisce un'altra promessa o la stessa cosa? Grazie per il vostro tempo, fatemi sapere se avete qualche riferimento per me per saperne di più su di esso. – florinmtsc

+0

Sicuramente la bandiera 'cache: true' fa la stessa cosa? Mi sto perdendo qualcosa? – zilj

0

Ho appena lo stesso identico problema e qui è il mio servizio

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 
Problemi correlati