resolution of promises observed across several controllers

I am looking to have two separate controllers trigger different functions once a set of promises are resolved within a service. I want to avoid making multiple HTTP requests, opting instead for just one request.

The scenario is as follows: a service makes a request and receives a promise. Controller1 should be able to detect when this promise resolves and then execute some code. Subsequently, Controller2 should also be able to pick up on the resolution of the same promise and run its own set of code. Essentially, I am aiming for multiple 'then()' methods triggering off the same promise but in different files. How can this be achieved?

While most examples demonstrate a single controller responding to a resolved promise, I need guidance on handling multiple controllers listening for the same resolution of a promise.

For reference, here is an excerpt from an article that I found insightful (I will be adding a 'mother controller' to better explain my specific case; it is important that the son service only makes the HTTP call once):

Son Service:

app.factory('SonService', function ($http, $q) {
    return {
        getWeather: function() {
            // Utilizing the $http API based on the deferred/promise structure from $q service
            // resulting in a promise being returned by default
            return $http.get('http://fishing-weather-api.com/sunday/afternoon')
                .then(function(response) {
                    if (typeof response.data === 'object') {
                        return response.data;
                    } else {
                        return $q.reject(response.data);
                    }

                }, function(response) {
                    return $q.reject(response.data);
                });
        }
    };
});

Father Controller:

 var makePromiseWithSon = function() {
            SonService.getWeather()
                .then(function(data) {
                    if (data.forecast==='good') {
                        prepareFishingTrip();
                    } else {
                        prepareSundayRoastDinner();
                    }
                }, function(error) {
                    prepareSundayRoastDinner();
              });
         };

Mother Controller:

var makePromiseWithSon = function() {
            SonService.getWeather()
                .then(function(data) {
                    if (data.forecast==='good') {
                        workInTheGarden();
                    } else {
                        sweepTheHouse();
                    }
                }, function(error) {
                    sweepTheHouse();
                });
         };

Answer №1

If you want your factory service to fetch the URL only once, save the httpPromise in the service.

app.factory('SonService', function ($http) {
    var weatherPromise;
    function getWeather() {
      return $http.get('http://fishing-weather-api.com/sunday/afternoon')
                .then(function(response) {
                    if (typeof response.data === 'object') {
                        return response.data;
                    } else {
                        // invalid response
                        throw response;
                    }

                }, function(response) {
                    // something went wrong
                    throw response;
                });
    }
    function sonService() {
      if (!weatherPromise) {
        //save the httpPromise
        weatherPromise = getWeather();
      }
      return weatherPromise;
    }
    return sonService;
});

Answer №2

To simplify things, you can create a service that caches data on outbound requests instead of caching return values. This approach is not specific to Angular but can easily be applied to it.

function SearchService (fetch) {
  var cache = { };
  return {
    getSpecificThing: function (uri) {
      var cachedSearch = cache[uri];
      if (!cachedSearch) {
        cachedSearch = fetch(uri).then(prepareData);
        cache[uri] = cachedSearch;
      }
      return cachedSearch;
    }
  };
}


function A (searchService) {
   var a = this;
   Object.assign(a, {
     load: function ( ) {
       searchService.getSpecificThing("/abc").then(a.init.bind(a));
     },
     init: function (data) { /* ... */ }
   });
}

function B (searchService) {
  var b = this;
  Object.assign(b, {
    load: function ( ) {
      searchService.getSpecificThing("/abc").then(b.init.bind(b));
    },
    init: function (data) { /* ... */ }
  });
}


var searchService = SearchService(fetch);
var a = new A(searchService);
var b = new B(searchService);

a.load().then(/* is initialized */);
b.load().then(/* is initialized */);

In this scenario, both A and B are using the same promise because the service they interact with returns the same cached promise.

If needed, you can ensure safety by caching a promise and returning new instances based on that cached promise.

// change 
return cachedSearch;

// to
return Promise.resolve(cachedSearch);

This way, each user receives a new instance while maintaining consistency with the original call stored in the cache.
You have the flexibility to further enhance this setup by adding cache expiration or invalidation mechanisms, among other possibilities.

Porting this concept to Angular is straightforward:

  • SearchService becomes a service
  • A and B become controllers
  • Use $http instead of fetch, but both work efficiently
  • With fetch( ).then(prepareData), handle JSON conversion; with $http, directly use response.data
  • Adopt $q methods instead of native promises
  • Switch from Object.assign to angular.extend
  • You've successfully translated this idea into both Angular and VanillaJS platforms!

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 tags integration in Angular and NodeJS

I want to enhance my question posts by including tags, similar to the ones found here. I am using html, angular, and node.js. My goal is for each tag entered by a user to be checked against the database to see if it already exists. If not, the user should ...

Incorporating z-index into weekly rows within the FullCalendar interface

I'm facing an issue where my detail dropdowns on events are being cropped by the following row. Is there a solution to adjust the z-index of each week row (.fc-row) in the monthly view, arranging them in descending order? For example, setting the z-i ...

Incorporating hidden input fields for date and time formatting in Vue.js 3

My goal is to include a hidden input field on a landing page template that captures the date and time in the format of: YYYY-MM-DD HH:MM:SS for each form submission. This is the hidden input field: <input type="hidden" name="hidden_submit_date" v- ...

How to effortlessly convert a JSON string into a JavaScript object

Hello everyone, I am working with DynamoDB and need to parse the JSON Object that is returned from a call in order to retrieve the password hash field. jsonString = JSON.stringify(data) console.log(jsonString) This is what the output looks like: {"Count ...

Verify if the expression can be found in the DIV element's style

Recently, I encountered a situation where a DIV contains a background image that is either set directly or generated as an SVG. I needed to figure out how to determine when the DIV contains the SVG-string. Here are my two DIVs: <div class="cover m ...

What is the best method for circumventing an express middleware?

I am currently working on an express application that utilizes several express routes, such as server.get('*' , ... ) to handle common operations like authentication, validation, and more. These routes also add extra information to the respon ...

Is there a jQuery or Javascript alternative to CSS Counter?

Can a counter be implemented that changes the text of a tag directly using jQuery/Javascript? For example, if there were two tags like this: <a>hello</a> <a>bye</a> After executing the jQuery/JS function, the result would be: < ...

Tips on implementing labels within loops in AngularJS

I find myself within an ng-repeat situation similar to the one below: <li ng-repeat="x in xs"> <form> <label for="UNIQUELABEL">name</label> <input id="UNIQUELABEL"> <label for="ANOTHERUNIQUELABEL" ...

What causes the picturesArray to remain consistently void?

const fetch = require("node-fetch"); let images = []; fetch('http://www.vorohome.com//images/assets/159314_887955.png') .then(response => response.buffer()) .then(buffer => { const data = "data:" + response.headers.get ...

Tips for addressing the issue of FormControlName being duplicated for every row in reactive forms

My reactive form contains columns and rows that can be dynamically added. However, when inspecting the form and adding multiple rows, the formControlName attribute repeats for each row. I am looking for a solution to have incrementing formControlNames for ...

What is the best way to toggle the color of the circle div in the center between yellow and white with every click within the circle?

Up to now, I have successfully accomplished this with just one click. I deleted the lightbulb image const sphere = document.getElementById("sphere"); sphere.addEventListener("click", event => { console.log("You clicked the ...

What is the reason for the creation of several monomorphic caches instead of a single polymorphic one

Currently, I am immersed in an enlightening article about monomorphism. Within this article, a fascinating code snippet caught my attention: function ff(b, o) { if (b) { return o.x } else { return o.x } } ff(true, { x: 1 }) ff(false, { x: 2 ...

The Google Maps listener event behaves as if it were clicked even though it is triggered by a mouseover

Two google.maps.event.addListener events are being added here google.maps.event.addListener(markerAcademicCenter, "mouseover", function (e) { markerIconAcademicCenter.url = 'MapIcons/Circle32.png' }); google.maps.event.addListener(markerAcade ...

Using JavaScript and HTML to show the current month, date, and year on a webpage

I am looking to display not only the time, but also the month, date and year. My attempted solution involved creating variables for the month, day and year, and then using getElementById. Here is an example of what I tried: var d = date.getDay(); var mn ...

What is the process for retrieving data from multiple checkboxes with just a single click?

I am struggling with an issue where all data is being displayed when clicking on one checkbox or selecting all. I would like it so that only the specific data related to a single checkbox is shown when selected. html file <div class="row"> ...

Why isn't client-side validation in ASP.NET Web Forms working when the field is filled via jQuery?

Within my ASP.Net WebForm, I have a mandatory field named Address. This particular field is filled in using jQuery based on the value entered into the Location field. Upon clicking the submit button without completing any fields, the client side validatio ...

How can we dynamically reference past elements in an array without using indices like "i-1", "i-2", and so on?

function play_tune(melody) { let played_notes = []; let durations = []; let positions = []; let wait_time = 0; let time_passed = 0; console.log("Melody initiation.") for (let key in melody) { played_notes.push(melody[key].note); dur ...

Welcome to the launch of OpenWeatherMap!

I just signed up for an account on the OpenWeatherMap website. My goal is to retrieve the current weather information for a specific location using the City ID API Call: http://api.openweathermap.org/data/2.5/weather?id=2172797&appid=myAPIKey How ca ...

What is the fate of a jQuery promise once its utility has been exhausted?

Is it true that jQuery has no way of knowing when an app is finished using a promise? It seems like the promise will continue to exist in memory until all references to it are gone. My assumption is that it remains active until it's resolved and the c ...

The "Key" function from Object.keys does not yield true when compared to an identical string

Object.keys(data).forEach((key) => { bannerData[key] = data[key][0]; console.log(key); if (key == "timestamp") { labels.push(data[key]); console.log("wtf"); console.log(labels); ...