Managing the invocation of a promise multiple times in AngularJS, and handling some specific exceptions

After previously asking a question on handling promises multiple times in AngularJS (AngularJS handle calling promise multiple times), I am facing a new challenge. This time, I need to retrieve a list of cities, but encounter an exception.

Similar to how countries were handled in my previous question, cities can also be called multiple times. However, this time around, I must implement a caching mechanism to prevent redundant calls for the same city data. While the solution from my old question blocked multiple calls altogether, I now need to allow certain calls - specifically when requesting cities for a new country.

My current dilemma is as follows: How can I effectively cache cities data to avoid making repeated calls for the same information? Essentially, my function needs to differentiate between requests for cities of a new country versus those already cached.

Below is the snippet of my service:

var cityCache = {};
vm.getCities = function (countryCode) {

    if (countryCode!=undefined && !cityCache[countryCode]) {

        vm.cityPromise = $http({
            method: 'POST',
            cache: true,
            url: API + '/api/Global/CountryCities',
            data: {
                "CountryCode": countryCode
            }
        }).then(function successCallback(response,countryCode) {
            if (errorHandler(response.data)) {
                console.log("cities come from ajax")
                cityCache[response.config.data.CountryCode] = response.data;
                console.log(cityCache)
                return response.data
            }
        });
    } else {
        vm.cityPromise = $timeout(function () {//I use this to get promise object
            return cityCache[countryCode]
        }, 0)
        console.log("cities comes from cache");
    }

    return vm.cityPromise;
}

To illustrate further, let's consider the following scenario: If the getCities function is called three times simultaneously with the following arguments - requesting cities in Germany, Ireland, and Germany once more - ideally, there should only be two network calls made. One for Germany and one for Ireland, effectively reducing redundancy.

Answer №1

Instead of reiterating the same response as your previous inquiry, I suggest implementing a mapping to the country code within the promise object. Additionally, it is crucial to address potential error scenarios, as previously mentioned.

let app = this;

app.countriesPromises = {};

function getCitiesByCountry(countryCode) {
    if (!app.countriesPromises[countryCode]) {
        app.countriesPromises[countryCode] = $http({
            method: 'POST',
            cache: true,
            url: API + '/api/Global/Countries',
        }).then(function successCallback(response) {
            if (errorHandler(response.data)) {
                console.log("ajax")
                return response.data;
            }
        });
    } else {
        console.log("cache")
    }
    return app.countriesPromises[countryCode];
}

Answer №2

Feel free to customize your promise creation process using your own method. Just make sure to include the $q service for injection.

var cityCache = {};

vm.getCities = function (countryCode) {

    var deferred = $q.defer();
    if (countryCode!=undefined && !cityCache[countryCode]) {
        vm.cityPromise = $http({
            method: 'POST',
            cache: true,
            url: API + '/api/Global/CountryCities',
            data: {
                "CountryCode": countryCode
            }
        }).then(function successCallback(response,countryCode) {
            if (errorHandler(response.data)) {
                cityCache[response.config.data.CountryCode] = response.data;
                deferred.resolve(response.data);
            }
            else{
                deferred.reject();
            }
        });
    } 
    else {
         vm.cityPromise = $timeout(function () {//Utilizing this approach to obtain the promise object
             deferred.resolve(cityCache[countryCode]);
        }, 0);
    }

    return deferred.promise;
}

Answer №3

Consider implementing the $q service provided by Angular:

Updated code to avoid making multiple calls for the same city:

Check out the FIDDLE here

Here's a snippet of the service in action:

.service("cityService", function($http, $q, $httpParamSerializerJQLike){
    var cityCache = {};
    return {
        getCities: function(countryCode){

            var promise = $q.defer();

            if (countryCode != undefined && !cityCache[countryCode]) {
                console.log("New city request");
                var data = $httpParamSerializerJQLike({
                    json: JSON.stringify({
                        name: countryCode + Math.random().toString(36).substring(7)
                    })
                });
                $http({
                    method: 'POST',
                    url:"/echo/json/",
                    data: data
                }).then(function(response) {
                    console.log("Service log", response.data);
                    cityCache[countryCode] = response.data;
                    var object = angular.extend({cache: false}, response.data);
                    promise.resolve(object);
                });
            } else {
                setTimeout(function(){
                    var object = angular.extend({cache: true}, cityCache[countryCode]);
                    promise.resolve(object);
                }, 1000)
            }
            return promise.promise;
        }
    }  
});

Answer №4

After encountering an issue, I managed to resolve it by implementing a promise object. Big thanks to @Luke Harper for his assistance in the past and present :) While his solution was on the right track, I needed to tweak it slightly to fit my application's requirements.

If you spot any flaws in my code, please feel free to reach out so I can make necessary adjustments.

Here is how I tackled the problem:

vm.cityPromise = {};
vm.getCities = function (countryCode) {
    vm.cityPromise["cityCache"] = countryCode;
    if (!vm.cityPromise[countryCode]) {
        if (countryCode != undefined && !cityCache[countryCode]) {
            vm.cityPromise[countryCode] = $http({
                method: 'POST',
                cache: true,
                url: API + '/api/Global/CountryCities',
                data: {
                    "CountryCode": countryCode
                }
            }).then(function successCallback(response, countryCode) {
                if (errorHandler(response.data)) {
                    cityCache[response.config.data.CountryCode] = response.data;
                    console.log("cities ajax, cityCache", cityCache)
                    return response.data
                }
            },function error (response){
                console.log ("error:",response)
            });
        } else {
            vm.cityPromise[countryCode] = $timeout(function () {
                return cityCache[countryCode]
            }, 0)
            console.log("getCities cache");
        }
    }
    return vm.cityPromise[countryCode];
}

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

Developing an interface that utilizes the values of an enum as keys

Imagine having an enum called status export enum status { PENDING = 'pending', SUCCESS = 'success', FAIL = 'fail' } This enum is used in multiple places and should not be easily replaced. However, other developers migh ...

Obtain a segment of the string pathway

In this scenario, there is a file path provided below. Unlike the rest of the URL, the last part (referred to as video2.mp4) regularly changes. The language used for this project is either Javascript or Typescript. file:///data/user/0/com.sleep.app/files/ ...

When using jQuery's POST method, the "done" event is triggered, however, no data is being sent

I've been working on implementing AJAX form submission and wrote a function to handle it when the button is clicked: function putUser() { $('button#putUser').on('click', function() { var user = $('input#user' ...

I'm having trouble grasping the concept of serving gzip-compressed JavaScript and CSS files

Why is it important to serve compressed JavaScript and CSS files? I understand that it reduces file size, but does the browser/webserver have to decompress them to read them? It's been mentioned that the webserver handles the compression. Does this me ...

The absence of the 'Access-Control-Allow-Origin' header is reported even though it is actually present

I have been attempting to send a POST request from one website to my own site. Despite allowing CORS access explicitly, every time I try to make the actual POST request, I am faced with the No 'Access-Control-Allow-Origin' header is present on th ...

Retrieve the variable declared within the event

How can I access a variable set in an event? Here is the code snippet: $scope.$on('event_detail', function (event, args) { $scope.id = args; console.log($scope.id); // This prints the correct value }); console.log($scope.id); // ...

After the update to the page, the DOM retains the previous element

I'm currently developing a Chrome Extension (no prior knowledge needed for this inquiry...) and I have encountered an issue. Whenever I navigate to a page with a specific domain, a script is executed. This script simply retrieves the value of the attr ...

Is there a way in AngularJS to deactivate a specific option?

Can anyone assist me in disabling a particular option in my JavaScript code? Here is the code snippet: <select ng-options="bus.id as bus.BU for bus in bustatuses" options-disabled="bus.value==4 for bus in bustatuses"> ...

Guide on creating multiple instances of vue-multiselect with a simple button click

I am trying to implement a vue-multiselect dropdown with the addition of a new dropdown upon clicking an "add more" button. However, I am currently unsure of the best approach to achieve this. Problem/Question: When adding 2 dropdowns, if the same option ...

Converting a mongoDB response array from a JavaScript object to a JSON string: a step-by

After developing a custom javascript API to retrieve data from a MongoDB database, the issue arose where the data is being returned as an array of objects instead of a simple JSON string. The current statement used for retrieving the objects is: return db ...

You can only set headers once during the initial request; any additional attempts to set headers will result in an

I encountered a specific issue with the error message "Can't set headers after they are sent". Here is the relevant code snippet: create: (request, response, next) -> socket = @app.socket # # This method will be used to call the right method ins ...

Comparison between Filament Group's loadCSS and AJAX technologies

The loadCSS library developed by Filament Group is widely recognized as the standard for asynchronously loading CSS. Even Google recommends its use. However, instead of using this library, some suggest utilizing ajax to achieve the same result. For example ...

What is the best way to access the inputName element using the ng-messages directive?

Is there a way to trigger error messages without knowing the input name? Take a look at the code snippet below: <input class="form-control" id="{{field.field_id}}" set-name="{{field.field_id}}" type="text" ...

Having trouble downloading the Chip component in Material-UI? Learn how to fix the download issue

I used the UI to upload a file, and now I want to download it either after some time or instantly. I tried implementing this using the <Chip> component, but it's not working. I need assistance in resolving this issue. Uploaded File: const data ...

When attempting to use the search bar to filter in ReactJs, an error occurs: TypeError - Unable to access properties of undefined (specifically 'filter')

When I receive data from my JSON server on the console, everything looks good. But when I try to type something in order to filter it, an unhandled error occurs with the message: 1 of 1 Unhandled Error Unhandled Runtime Error: TypeError: Cannot read prop ...

Access scope information when clicking with ng-click

I am currently using a php api to update my database, but I want the ability to choose which item gets updated with ng-click. app.controller('ReviewProductsController', function ($scope, $http) { $scope.hide_product = function () { ...

Different ways to activate the system bell in Node.js

Currently, I have a custom nodejs script running for an extended period and I'm seeking a way to receive a notification once the script finishes its execution. Is there a method in nodejs that can be used to activate the "System Bell" alert? ...

Angular UI modal does not have access to the parent scope

Utilizing Angular UI modal to implement a modal in my current project has been successful. However, I encountered an issue when trying to reference a variable in the parent scope. You can view the code on this Plunker link. It appears that the modal is u ...

Difficulty encountered when applying date filtering on a specific filter in the MUI-DataGrid

Software Version: "@mui/x-data-grid": "^6.2.1" I have a script that generates columns for the DataGrid as shown below (only including relevant code) if (prop === "date" || prop === "dateModified" || prop === "n ...

When the jQuery document is ready, it typically returns null, but the console can still access and display

I have encountered an issue while working on a solution within a CMS (EPiServer). When I utilize console.log to check my object, it displays a null value. $(document).ready(function () { console.log("$('.EPiRequester').html() =" + $('. ...