Preventing multiple $ http requests in Angular. Is there a better way?

I built a somewhat complicated method for returning resources via $ http.

The method returns a promise and then checks my local cache if resources exist. If this happens, it will return cached resources if it does not make a $ http request. This works fine after caching the resource, but I have several functions through an application that clicks this method at boot, and each of them will make an HTTP request because the resources have not yet been returned and cached.

I came up with a simple check that fixes this, but I feel that there should be a better way. I added a boolean value that is set to True if the method is in the middle of getting the resource, and if I enable the method with a timeout of one and a half seconds, to give time for the request. The code is below.

So is there a better way?

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); }); } }); } 
+6
source share
2 answers

Firstly, I do not consider it necessary to wrap HttpPromise in another promise. With success/error methods deprecated , you should rely solely on the then() method and consider HttpPromise as just a regular promise.

To make sure that the request is sent only once, you can actually track the first HttpPromise that you create and return the same promise on subsequent calls to the function.

Here is a service that will take an API endpoint as an argument and ensure that only one request is sent to that 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; })); }; } ]); 

Using

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

Here is a demon. In developer tools you can see only one request.

+20
source

I just have the exact same problem and here is my service

 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 
0
source

All Articles