Is it possible to avoid sending multiple $http requests in Angular? Are there more efficient methods available?

I have developed a rather intricate method for retrieving resources using $http.

This method returns a promise and first checks my local cache to see if the resources are already available. If they are, it will return the cached data; if not, it will make the $http request. This works well once the resource has been cached, but I have several functions in the application that call this method on load, and each one of them ends up making the http request because the resources have not been returned and cached yet.

I devised a simple check to address this issue, but I can't help but think there might be a more efficient solution. I introduced a boolean variable that is set to true while the method is in the process of fetching the resource. If it is, I resolve the method with a timeout of half a second to allow the request to complete. The code snippet is provided below.

So, is there a better approach?

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

Answer №1

Initially, there is no need to wrap the HttpPromise within another promise. Since the success/error methods have been deprecated, it is advised to rely solely on the then() method and treat the HttpPromise as a standard promise.

To ensure that the request is only sent once, you can keep track of the initial HttpPromise created and return the same promise for subsequent function calls.

Below is a service that takes an API endpoint as an argument and guarantees that only one request is made 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;
        }));
    };
  }
]);

Usage

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

// sends an HTTP request
$httpOnce('https://api.github.com/').then(log);
// reuses the previous promise without sending a new request
$httpOnce('https://api.github.com/').then(log);

// ...
// Upon completion of the HTTP request, both promises above are resolved
// ...

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

Take a look at the demo to see in the developer tools that only one request is actually made.

Answer №2

I encountered the same issue and found a solution:

app = angular.module('MM_Graph')

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

  callWithCache: (method, params, callback)=>
  
    cacheKey = "#{method}_#{JSON.stringify(params)}"
    
    if not @.cache.get cacheKey
      deferred = @.$q.defer()
      @.cache.put cacheKey, deferred.promise
      
      onDataReceived = (data)->
        deferred.resolve(data)
        
      methodParams = params.concat(onDataReceived)
      
      @.API[method].apply(null, methodParams)
      
    @.cache.get(cacheKey).then (data)->
      callback (data)

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

  loadData: (callback)=>
    if not (@.$routeParams.project and @.$routeParams.team)
      return callback()

    if (@.$routeParams.project is @.project and @.$routeParams.team is @.team)
    
    else
      @.clearData()
      @.project = @.$routeParams.project
      @.team    = @.$routeParams.team
      @.projectSchema (schema)=>
        @.dataScore (scores)=>
          @.teamGet (data)=>
            @.scores = scores
            @.schema = schema
            @.data   = data

            @.deferred.resolve()

    @.deferred ?= @.$q.defer()
    @.deferred.promise.then ->
      callback()

  dataScore    : (             callback) => @.callWithCache 'data_Score'       , [@.project, @.team      ], callback
  projectSchema: (             callback) => @.callWithCache 'project_Schema'   , [@.project              ], callback
  radarFields  : (             callback) => @.callWithCache 'data_Radar_Fields', [@.project              ], callback
  radarTeam    : (targetTeam, callback) => @.callWithCache 'data_Radar_Team'  , [@.project, targetTeam ], callback
  teamGet      : (             callback) => @.callWithCache 'team_Get'         , [@.project, @.team      ], callback

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

app.service 'teamInfo', ($routeParams, $rootScope, $cacheFactory, $q, API)=>
  return new TeamInfo $routeParams, $rootScope, $cacheFactory, $q, API

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Click events on jQuery's cloned elements are not retained when the elements are removed and appended to a container for the second time

Take a look at this fiddle: https://jsfiddle.net/L6poures/ item.click(function() { alert("It's functioning") }) $("#container").append(item) var stored = item.clone(true, true) function add_remove() { $("#container").html("") $("#conta ...

Tips for altering objects within an array

I am dealing with an array of objects that looks like this: const items = [ {name: 'Sam', amount: '455gbjsdbf394545', role: 'admin'}, {name: 'Jane', amount: 'iwhru84252nkjnsf', role: 'user'}, ...

What steps can you take to convert your current menu into a responsive design?

I am currently developing a website using PHP, HTML, and CSS. The site is not responsive, and I want to make the menu responsive. My global navigation and admin interface are not adapting properly as they are designed using tables. Is there a method I can ...

Unsure how to proceed with resolving lint errors that are causing changes in the code

Updated. I made changes to the code but I am still encountering the following error: Error Type 'String' is not assignable to type 'string'. 'string' is a primitive, but 'String' is a wrapper object. It is recom ...

php json with multiple dimensions

Unable to retrieve the deepest values from my json object, which contains an array of images listed as: { "imgs":[ { "Landscape":{ "2":"DSCF2719.jpg", "3":"DSCF2775.jpg", "4":"IMG_1586.jpg", ...

I am interested in using a loop in Angular to highlight my div element

Enhancing my comprehension regarding the mentioned images. If I don't select anything within the div property, the default style (css) should appear like this, at least when one div is selected. However, the issue arises when unable to select. This ...

Using Asynchronous JavaScript and XML (AJAX) to make calls to a web service

My program is showing an internal server error var parameters = "<?xml version='1.0' encoding='utf-8'?>" + "<soap:envelope xmlns:xsi='ttp://www.w3.org/2001/xmlschema-instance' xmlns:xsd='http://www.w3. ...

What is the best way to outline the specifications for a component?

I am currently working on a TypeScript component. component @customElement("my-component") export class MyComponent extends LitElement { @property({type: String}) myProperty = "" render() { return html`<p>my-component& ...

Tips for generating a universal regulation in vee-validate version 3

Is it possible to create a universal validation rule that can be applied to multiple elements? universalRule: { required:'required', min:'min', etc.. } On the form <ValidationProvider name="universalRule" rules=&qu ...

What method would you recommend for modifying HTML text that has already been loaded using JSP?

Is there a way to update text on an HTML document without reloading the entire page? I'm looking to create a simple "cart" functionality with 5 links on a page. When a link is clicked, I want it to increment the "items in cart" counter displayed on th ...

What is the best method for managing file storage and retrieval in web applications that only run on the client-side?

I'm currently working on an application where users can create their own data. Without a backend or database, all the information is stored within the user's session. My preferred solution involves: User interacts with the app, clicks "Save", ...

Having trouble with table sorting in Jquery?

I am a beginner in the realm of Jquery and web programming. Recently, I attempted to implement the tablesorter jquery plugin for one of my projects but encountered issues with making it work properly. In search of a solution, I turned to Stack Overflow. C ...

Implementing JavaScript Code in TypeScript

Every JavaScript code should also be valid in TypeScript, but when attempting to run the following code snippet below, an error is triggered. Could someone convert this JavaScript code into TypeScript? Error: 20:9 - TS2531 Error: Object is possibly 'z ...

Formatting dates in JavaScript

When using the jQuery datepicker, my goal is to set a minimum and maximum date range for users to select from. I also need to ensure that the date format is based on the user's locale. In this case, there are two date fields: Start date and End date. ...

Transferring data between functions within the same controller in AngularJS

I have implemented two functions to handle the timing of a process, one for starting and one for stopping. Here is the code snippet: $scope.start = function(data) { $scope.time = { id: '', mainId: data.id, start: &ap ...

Launching an embedded webpage in a separate tab within the main browser window

In my current setup, I have implemented an iframe within the main window. The iframe includes a form where users can input data and submit it. Currently, I achieve the submission with the following code: var openURL = '/results/finalpage'; windo ...

Using javascript to quickly change the background to a transparent color

I am in the process of designing a header for my website and I would like to make the background transparent automatically when the page loads using JavaScript. So far, I have attempted several methods but none seem to be working as desired. The CSS styles ...

Utilizing middleware with express in the proper manner

Hello there! I'm just double-checking to see if I am using the correct method for implementing middleware in my simple express app. Specifically, I am trying to ensure that the email entered during registration is unique. Here's an example of wha ...

Why does this particular check continue to generate an error, despite my prior validation to confirm its undefined status?

After making an AJAX call, I passed a collection of JSON objects. Among the datasets I received, some include the field C while others do not. Whenever I try to execute the following code snippet, it causes the system to crash. I attempted using both und ...

Customizing line charts with D3.js and AngularJS for ultimate visual impact

Working on a project involving the creation of a line chart using D3.js library and AngularJS within an Ionic framework. I am looking to customize the color of data points based on the Y-axis values. For example, if the value falls between 0-30, I want t ...