Storing a promise object within an AngularJS service

Incorporating dynamic loading of a static resource in AngularJS using Promises is my goal. The challenge lies in having multiple components on the page that may or may not require the same static resource from the server, depending on which are displayed dynamically. Once loaded, the resource can be cached for the duration of the application.

Although I have implemented this mechanism, my proficiency in Angular and Promises is still developing. Therefore, I am seeking validation regarding the effectiveness of my solution/approach.

var data = null;
var deferredLoadData = null;

function loadDataPromise() {
  if (deferredLoadData !== null)
    return deferredLoadData.promise;

  deferredLoadData = $q.defer();

  $http.get("data.json").then(function (res) {
    data = res.data;
    return deferredLoadData.resolve();
  }, function (res) {
    return deferredLoadData.reject();
  });

  return deferredLoadData.promise;
}

This approach ensures that only one request is sent, and all subsequent calls to `loadDataPromise()` will receive the initial promise. This method appears to handle requests that are in progress or already completed successfully.

However, I am uncertain whether caching Promises is considered an optimal solution. Is this practice advisable?

Answer №1

Is this the correct approach to solving the problem?

Absolutely. Utilizing memoization on functions that return promises is a widely used technique to prevent redundant execution of complex asynchronous tasks. The promise simplifies caching by treating ongoing and completed operations as the same promise for the resultant value.

Is this the optimal solution?

No, relying on a global variable like `data` and resolving with `undefined` does not align with the intended usage of promises. It is recommended to fulfill the promise with the actual result from `data`, enhancing code readability and efficiency:

var dataPromise = null;

function getData() {
    if (dataPromise == null)
        dataPromise = $http.get("data.json").then(function (response) {
           return response.data;
        });
    return dataPromise;
}

Instead of using

loadDataPromise().then(function() { /* use global */ data })
, you can simply leverage
getData().then(function(data) { … })
.

To refine the pattern further, consider encapsulating `dataPromise` within a closure scope and be mindful of handling multiple promises when `getData` requires parameters such as a URL.

Answer №2

To simplify this task, I developed a service named defer-cache-service that eliminates the need for repetitive code. It is written in Typescript, but you can also access the compiled JavaScript file on Github. Check out the source code.

Here's an example:

function loadCached() {
   return deferCacheService.getDeferred('cache.key1', function () {
      return $http.get("data.json");
   }); 
} 

and then use it like this:

loadCached().then(function(data) {
//...
});

It's important to note that if multiple parts are calling the same loadDataPromise simultaneously, you need to include this check:

if (defer && defer.promise.$$state.status === 0) {
   return defer.promise;
}

Otherwise, duplicate calls to the backend will be made unnecessarily.

Answer №3

This specific pattern is designed to store in cache the initial result and then retrieve it from the cache whenever the function is called again.

const cachedFunction = (cachedData => {
  return function(){
    // if there is no data in the cache yet, store a promise in the "cachedData" variable
    if( !cachedData ){
        cachedData = new Promise(function(resolve, reject){
            setTimeout(() => {
                resolve('foo');
            }, 2000);
        });
    }
    return cachedData;
  }
})();

cachedFunction().then(console.log);
cachedFunction().then(console.log);

Explaination:

To implement this, you wrap your function with another immediately-invoked function that returns the original function, ensuring that the local variable cachedData is encapsulated within the wrapper function. This guarantees that the value of the variable remains consistent every time the function is called after the first invocation.

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

Having trouble accessing the href attribute after clicking on an <a> tag with JavaScript

My issue lies in retrieving the href attribute of <a> tags when there is another element nested inside them: Here's the code snippet: const $elems = document.querySelectorAll('.content-container a') var elems = Array.from($elems) e ...

Traverse an SVG element using JavaScript

I have a beautiful star SVG that I created, and I want to use it instead of styling a span to create floating bubbles in my div. However, I'm facing some challenges in adapting my Javascript loop to incorporate the SVG for creating the bubbles, as opp ...

Viewing saved information prior to saving - JavaScript

I'm looking for a solution to allow users to preview captured records before they are inserted. Any suggestions on how to achieve this? HTML.html <form id="view" method="POST" class="formular"> <label>Name</label> ...

Unable to retrieve or remove cookie sent from Express on client side

My Express server is sending a cookie to the client that is not httpOnly. Despite this, the client is unable to access the cookie through document.cookie or see it in the Application tab on chrome dev tools. Interestingly, I am able to view the cookie in C ...

What is the process for fetching and storing a binary file video in Flask's Blob?

I've been dealing with a video encoding issue for the past few days and I need some help. Objective: Capture a video from my laptop's webcam using a VueJS frontend application. Send this video to a Python Flask app on the backend via FormData u ...

Having trouble importing components in NativeScript Vue?

I am just starting to explore the world of NativeScript and working on my first project using it. I am facing an issue while trying to incorporate a header component into my main App. Unfortunately, when I run the iOS emulator, the header does not appear o ...

Using Selenium Webdriver with Node.js to Crop Images

Currently, I am using selenium-webdriver in combination with nodejs to extract information from a specific webpage. The page contains a captcha element from which I need to retrieve the image. Unfortunately, all the code snippets and solutions I came acros ...

Creating dynamic "floating divs" with consistent width but varying heights, alignment challenge

Seeking assistance to solve the following challenge: Description of Issue: We are dealing with dynamically generated "floating divs" that have equal width but varying heights based on their content. The "Parent container" will have different width parame ...

Employing various Class Methods based on the chosen target compiler option

Is there a way to instruct TypeScript to utilize different implementations of methods within the same class, based on the specified target option in the tsconfig.json file? I am currently transitioning one of my scripts to TypeScript to streamline managem ...

Obtain the selected row's ID by leveraging angular.js with either ngSelect or ngOptions

I'm currently working on an angular.js project where I have a dynamic table displaying study results. I want to enhance this by allowing users to view more detailed data about each specific study when they click on the corresponding row in the table. ...

Resetting the internal state in Material UI React Autocomplete: A step-by-step guide

My objective is to refresh the internal state of Autocomplete, a component in Material-UI. My custom component gets rendered N number of times in each cycle. {branches.map((branch, index) => { return ( <BranchSetting key={ind ...

Puppeteer's flawed performance leads to the generation of low-quality

Currently, I am utilizing puppeteer to generate a PDF from my static local HTML file. However, the resulting PDF is turning out to be corrupted. When attempting to open the file using Adobe Reader, an error message pops up stating 'Bad file handle&apo ...

Try to steer clear of using $watch for changes in directive attributes

One of the directives I've created is <resized-image>, which includes an attribute known as id. This directive relies on this id to request an image object from an API. The primary function of this directive is to serve as a header image for an ...

Challenges with retrieving all text records in a profile using ethers.js

Currently, I am facing difficulties in retrieving all ENS text records from a profile due to issues with the logic or potential approaches... With ethers.js v6, my current strategy involves fetching all the log data and then sifting through it. However, t ...

Implementing a class addition on focus event using Angular 2

Currently, I am in the process of upgrading an Angular 1 application to Angular 2 and encountering an issue with one of my existing directives. The task at hand is straightforward. When an input field is focused, a class should be added (md-input-focus) a ...

Encountering a Next.js event type issue within an arrow function

After creating my handleChange() function to handle events from my input, I encountered an error that I'm unsure how to resolve. Shown below is a screenshot of the issue: https://i.sstatic.net/fWJA2.png I am currently working with Next.js. In React ...

Issue with slideshow counter persisting across multiple arrays

I am experiencing an issue with my simple slideshow type application where it consists of multiple parts on the page. Each slideshow has its own array containing specific information for each slide. The problem arises when I cycle through one slideshow to ...

I need to extract information from the database and save all entries from the form in order to send them to myself. This includes calculating the real-time multiplication of weight and pieces

For a project, I need to gather contact data from the client, and then populate a MySQL database with the information to create new rows in a table. There's an additional requirement where I have to calculate the total weight of element pieces multip ...

The images in the React slick carousel appear to be slightly out of

I'm experiencing blurriness in my carousel when it animates with card items. Despite searching for a solution, I haven't found one yet. My tech stack includes Next.js and TypeScript. const ref = useRef<any>(); const settings = { arro ...

Can anyone suggest a way to iterate over an object and substitute spaces in the keys?

I need to iterate through a dynamically-created object that contains properties with values. The issue is that the property names in this dynamic object contain spaces, which are not allowed in JavaScript object properties. How can I loop through this ob ...