Retrieving data efficiently: utilizing caching in an AngularJS service to store data fetched from a single $http request

Forgive me if this question seems simple or silly, but I find myself in a new scenario and need some guidance. In my AngularJS app, I have a service with 4 methods that share about 80% of the same functionality and code. I want to optimize this and make it more efficient. Here is a simplified version of my service:

    .factory('townDataService', function ($http) {

        var townList = {};

        townList.getTownList = function () {

            return $http({method: 'GET', url: '/api/country/cities'})
                .then(function (response) {

                    // Formatting the response data here...
                    var returnArray = [];
                    // Looping through the countries
                    var JsonData = response.data;

                    for (key in JsonData['countries']) {
                         // Formatting code...
                    }
                    // End of repeated code

                    return returnArray;

                });
        };


     townList.getCurrentTown = function (place) {

            return $http({method: 'GET', url: '/api/country/cities'})
                .then(function (response) {

                    // Formatting the response data here...
                    var returnArray = [];
                    // Looping through the countries
                    var JsonData = response.data;

                    for (key in JsonData['countries']) {
                         // Formatting code...
                    }
                    // End of repeated code

                    // Further working with the returnArray...
                    for (var i = 0; i < returnArray.length; i++) {
                        // Do something
                    }
                    return currentTown;

                });
        };


        townList.getCurrentCountry = function (place) {

            return $http({method: 'GET', url: '/api/country/cities'})
                .then(function (response) {

                    // Formatting the response data here...
                    var returnArray = [];
                    // Looping through the countries
                    var JsonData = response.data;

                    for (key in JsonData['countries']) {
                         // Formatting code...
                    }
                    // End of repeated code

                    // Further working with the returnArray...
                    for (var i = 0; i < returnArray.length; i++) {
                        // Do something
                    }
                    return currentCountry;

                });
        };

        return townList;

    }
);

I realize that I am repeating the same $http 'GET' request and formatting code in each method, which is not efficient. What is the best approach to centralize this functionality in its own function so that the GET request is only made once, but each method still returns a promise? Should I store the results of the $http request in a variable and inject it into each method before formatting the data? Or should I utilize a $cacheFactory?

I apologize if my question is unclear, and I appreciate any help you can provide.

Thank you.

Answer №1

As you mentioned, there are several ways to refactor this code for better organization and efficiency. One approach could involve separating the HTTP handling and caching into a dedicated service, while the formatting logic could be placed in a separate method:

The HTTP handling service:

.service('townHttpService', function($http, $q) {
    var cache;

    function getCities() {
        var d = $q.defer();
        if( cache ) {
            d.resolve(cache);
        }
        else {
            $http({method: 'GET', url: '/api/country/cities'}).then(
                function success(response) {
                    cache = response.data;
                    d.resolve(cache);
                },
                function failure(reason) {
                    d.reject(reason);
                }
            });
        }
        return d.promise;
    }

    function clearCache() {
        cache = null;
    }

    return {
        getCities: getCities,
        clearCache: clearCache
    };
})

The formatter:

.service('townFormatter', function() {
    return function townFormatter(jsonData) {
        // Code for formatting the response...
        var returnArray = [], key;
        // Loop through the countries
        for (key in jsonData['countries']) {
             // Formatting logic...
        }
        return returnArray; // The formatted array
    };
})

Refactoring your townDataService using the above services:

.factory('townDataService', function (townHttpService, townFormatter) {

    var townList = {};

    townList.getTownList = function () {
        return townHttpService.getCities().then(townFormatter);
    };

    townList.getCurrentTown = function (place) {
        return townHttpService.getCities().then(townFormatter).then(function(cityList) {
            var currentTown;
            for (var i = 0; i < cityList.length; i++) {
                // Perform operations
            }
            return currentTown; // Resulting string
        });
    };

    townList.getCurrentCountry = function (place) {
        return townHttpService.getCities().then(townFormatter).then(function(cityList) {
            var currentCountry;
            for (var i = 0; i < cityList.length; i++) {
                // Perform operations
            }
            return currentCountry; // Resulting string
        });

    return townList;
})

Answer №2

It seems like you have two inquiries regarding streamlining code and implementing an efficient caching strategy.

Streamlining code: The method townList.getTownList appears to be a central function, with the other two methods serving as extensions of it. In order to eliminate duplicate code,

townList.getCurrentTown = function(place) {
  var towns = townList.getTownList();
  for (var i = 0; i < returnArray.length; i++) { //additional stuff
  }
  return currentTown;
};


townList.getCurrentCountry = function(place) {
  var towns = townList.getTownList();
  for (var i = 0; i < returnArray.length; i++) { //additional stuff
  }
  return currentCountry;
};

Implementing caching: Currently, the http call is only made in townList.getTownList, making it a suitable place to incorporate a caching mechanism. The decision to cache data depends on whether it is a one-time fetch or requires periodic refresh. One-time fetch: Simply enable caching in the http call

$http({method: 'GET', url: '/api/country/cities', cache:true});

Refresh based on request: One approach is to introduce a refresh parameter to trigger data refresh when necessary. If the refresh flag is true or the townList is empty, new data will be fetched.

var srvc = this;
var townList;
townList.getTownList = function(refresh ) {
  if (refresh || !townList) {
    srvc.townList = $http({
      method: 'GET',
      url: '/api/country/cities'
    })
      .then(function(response) {
        var returnArray = [];
        var JsonData = response.data;
        for (var key in JsonData.countries) {}
        return returnArray; // the data is returned as an array without any formatting
      });
  }

  return townList;
};

Answer №3

To optimize your code and streamline its performance, consider caching your GET response and refactoring to eliminate redundancy. Here is an example of how you can achieve this:

.factory('cityDataService', function ($http) {
    var getCitiesAsync = function(){
        return $http({method: 'GET', url: '/api/country/cities', cache:true});
    };

    var cityList = {};

    cityList.getCityList = function () {
        return getCitiesAsync().then(prepareCityList);
    };

    var prepareCityList = function(response){
        //extract cities and customize as needed
        return result;
    };

    ...

Using $cacheFactory might be unnecessary in this context, as the built-in cache option can suffice for simpler scenarios.

Answer №4

To prevent potential timing issues, it may be advantageous to enhance the solution by extending it slightly:

function retrieveCities() {
        var deferred = $q.defer();
        if( cityCache ) {
            deferred.resolve(cityCache);
        }
        else {
            $http({method: 'GET', url: '/api/country/cities'}).then(
                function success(response) {
                    if (!cityCache) {
                        cityCache = response.data;
                    }
                    deferred.resolve(cityCache);
                },
                function failure(reason) {
                    deferred.reject(reason);
                }
            });
        }
        return deferred.promise;
    }

Upon subsequent calls to the webservice that result in success, it is important to check if the cityCache variable was set while awaiting the server response. If the cache has been populated, we can simply return the existing value. This approach ensures that the variable cityCache is not overwritten with new data if multiple requests are made:

function success(response) {
    if (!cityCache) {
        cityCache = response.data;

While this may not always pose an issue, it can be especially beneficial in scenarios where identical object instances are required (such as in data binding) to guarantee that data is fetched only once!

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

Exploring AngularJS 1.5+ Unit Testing: Harnessing the Power of $componentController and Embracing Double

I'm currently encountering an issue with my testing code. They say a picture is worth a thousand words, so here's an example. describe('Initialization', () => { let $componentController, scope; beforeEach(inject((_$componen ...

Sending files via an Ajax request triggers an unexpected page refresh

I have limited experience with ajax, but I am encountering an issue when trying to send form data to an ASP controller. Scenario 1 When I set data in the ajax request to $(this).serialize() All form data is successfully posted to the controller withou ...

Combining multiple JSON objects in an array into one single object with the help of jQuery

My array consists of JSON objects like the ones shown below: [{ "Name": "Nikhil", "Surname": "Agrawal" }, { "profession": "java developer", "experience": "2 years" }, { "company": "xyz", "city": "hyderabad" }] What I aim to achiev ...

Performing an API request and saving the response data into a

I’m struggling to figure out how to make an API call with request in MongoDB using Mongoose. I’m new to this topic and could really use some help! Here is my test.js file, does anyone have any ideas on how to solve this issue? My goal is to save the AP ...

Tips for structuring dependencies within an Angular module

When developing my angular application, I implement the application component segmentation logic by organizing my folders in the following structure: app.module.js app.config.js components ---- core -------- core.module.js -------- etc... ---- component1 ...

What could be causing the failure of this web component using shadow DOM when the HTML code includes multiple instances of the component?

Recently, a question was raised on this platform regarding the use of Selenium to access the shadow DOM. Curious about this topic, I delved into the MDN article Using shadow DOM and decided to replicate the code on my local machine. Surprisingly, it worked ...

What is the process for incorporating a pure Angular application into a Visual Studio solution?

Is there a way to incorporate an Angular app using pure JavaScript/Less/Node files into a Visual Studio solution as the UI layer without generating the default infrastructure that VS typically includes for empty projects? ...

Sending data to API using AngularJS Http Post

Upon clicking "Add new User", a modal pop-up will appear with a form containing a text field and a checkbox. However, upon clicking the create button, the data is not being posted to the API and the modal pop-up remains open without closing. I would like ...

Reorganizing form input array following the dynamic addition and deletion of fields

Currently, I am in the process of developing a custom WordPress widget that allows users to add and remove blocks of input fields in the widget back-end. While I have managed to make the add and remove functionality work smoothly, I am facing an issue when ...

Creating metadata for a node/npm module build

Looking for a solution to output JSON with build date and updated minor version number using grunt and requirejs for our application. It seems like this would be a common requirement. Any existing tools that can achieve this? ...

Should each of them be opened in a new connection if web and worker processes are separated?

In my current NodeJS web app project, there are two processes in play - a web process and a worker process. These processes communicate via AMQP. To start the application, I run two scripts: one for the web process called server.js, and another for the wor ...

Tips for positioning a chat box at the bottom of a v-card's visible area

My goal is to create a chat app using Vuejs 3 and Vuetify 3, but I'm encountering an issue aligning the chatbox with the v-card component. Instead of being positioned at the bottom of the v-card, the chatbox (green) appears at the bottom of the page. ...

Gathering all the bugs and issues related to AngularJS and Ionic crashes

This inquiry is quite simple. Are there any web services available that offer a framework for aggregating all client crash reports? Our clients are built with AngularJS and Ionic, and we are seeking a standardized method for gathering these error logs. ...

Is there a way to customize the total time out for the v-progress-loader-circular?

After attempting to adjust the time value to 30 seconds and then resetting it, I found that the circular progress would always stop at 100. My goal is for it to count up to 30, with 30 being the maximum count. Even though I did manage to reset it after 30 ...

Ways to change the color of a button when it is clicked?

I am attempting to change the color of a button on another button click, but it doesn't seem to be working. function show_col(){ console.log("hello"); var path=localStorage.getItem(".physics_section #color1 button"); $(''+ ...

The combination of Nest, Fastify, Fastify-next, and TypeOrm is unable to locate the next() function

In my attempt to set up Nest with Fastify and Next using the fastify-next plugin, everything went smoothly until I added TypeOrm for MongoDB integration. Upon loading the AppModule, Nest throws an error indicating that the .next() function cannot be found ...

Understanding the reasons behind 'undefined' res.body in Express

Why does the response body show as undefined in Express? How can I access the response body properly? Is something going wrong here? import {createProxyMiddleware} from 'http-proxy-middleware' import bodyParser from 'body-parser' impor ...

Enhancing a React modal to enable user input for updating state variables

Is there a way to utilize user input from the page to dynamically create elements in a React.js application? In my app.js file, I have defined some constants at the beginning for mock data: const book1 = [ {id: uuid(), content: 'reflections&apos ...

Issue with Next.js: Callback function not being executed upon form submission

Within my Next.js module, I have a form that is coded in the following manner: <form onSubmit = {() => { async() => await requestCertificate(id) .then(async resp => await resp.json()) .then(data => console.log(data)) .catch(err => console ...

How can I use `app.js` in Zendesk to connect to an external API using the complete URL

As someone new to developing Zendesk apps, I've been following the step-by-step guide available here. To Summarize I'm facing an issue with passing external API URLs to the AJAX call syntax within Zendesk's app.js file. You can find my sim ...