AngularJS
REST API
Performance Optimization
Asynchronous Calls
JavaScript

AngularJS Avoid calling same REST service twice before response is received

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In AngularJS applications, duplicate HTTP calls are a common source of latency and confusing UI behavior. They usually happen when users click quickly or when multiple components request the same resource at nearly the same time. A good solution is to deduplicate in-flight requests and expose clear loading state to the view.

Why Duplicate Calls Happen

AngularJS executes asynchronous code quickly, and each controller or component can trigger its own request lifecycle. If two parts of the page call the same endpoint before the first response finishes, the server receives redundant traffic.

A simple example:

javascript
1app.controller("UserCtrl", function ($scope, UserService) {
2  $scope.load = function () {
3    UserService.getProfile();
4  };
5});

If the button is clicked twice, getProfile runs twice unless the service guards against reentry.

You can reduce accidental repeats at the UI level, but the durable fix belongs in the service layer because many callers may reuse the same method.

Coalesce In-Flight Requests with a Promise Cache

A practical pattern is to keep one in-flight promise per request key. If another call arrives while the first call is pending, return the same promise instead of issuing a new request.

javascript
1app.factory("UserService", function ($http, $q) {
2  var inFlight = {};
3
4  function getProfile(userId) {
5    var key = "profile:" + userId;
6
7    if (inFlight[key]) {
8      return inFlight[key];
9    }
10
11    inFlight[key] = $http
12      .get("/api/users/" + userId)
13      .then(function (res) {
14        return res.data;
15      })
16      .finally(function () {
17        delete inFlight[key];
18      });
19
20    return inFlight[key];
21  }
22
23  return {
24    getProfile: getProfile,
25  };
26});

This design gives three benefits:

  1. only one network request per key while pending,
  2. all callers receive the same resolved data,
  3. cleanup happens in finally so the next refresh can run.

If your endpoint returns large payloads and does not change often, add a second cache for resolved data and a time to live value. That avoids network calls even after the first request completes.

Guard the UI and Keep State Explicit

Service deduplication is important, but users still need visual feedback. Disable action controls while loading so interaction remains clear.

javascript
1app.controller("UserCtrl", function ($scope, UserService) {
2  $scope.loading = false;
3  $scope.profile = null;
4
5  $scope.loadProfile = function (userId) {
6    if ($scope.loading) {
7      return;
8    }
9
10    $scope.loading = true;
11    UserService.getProfile(userId)
12      .then(function (data) {
13        $scope.profile = data;
14      })
15      .catch(function () {
16        $scope.error = "Could not load profile.";
17      })
18      .finally(function () {
19        $scope.loading = false;
20      });
21  };
22});

Template side:

html
1<button ng-click="loadProfile(42)" ng-disabled="loading">
2  <span ng-if="!loading">Load profile</span>
3  <span ng-if="loading">Loading...</span>
4</button>

The controller guard protects against fast repeated clicks, and the service guard protects against multi-caller duplication.

Add Request Cancellation for Rapid Navigation

In list and search pages, users may trigger many requests by typing quickly or switching views. You can cancel stale requests by passing a timeout promise to $http.

javascript
1app.factory("SearchService", function ($http, $q) {
2  var canceller = null;
3
4  function search(term) {
5    if (canceller) {
6      canceller.resolve();
7    }
8
9    canceller = $q.defer();
10
11    return $http.get("/api/search", {
12      params: { q: term },
13      timeout: canceller.promise,
14    });
15  }
16
17  return { search: search };
18});

This does not deduplicate by key, but it prevents wasted work on requests you no longer need.

Common Pitfalls

A common mistake is placing the in-flight map in a controller, which breaks deduplication across pages. Keep it in a singleton service.

Another issue is forgetting to delete request keys when an error occurs. Always clean up in finally, not only in success handlers.

Some teams rely only on ng-disabled and think duplicates are solved. That helps for button clicks, but programmatic calls from route hooks or watchers can still issue repeated requests. Keep both UI guard and service guard.

Summary

  • Duplicate REST calls usually come from rapid user actions or multiple callers.
  • Use a service-level in-flight promise cache keyed by request identity.
  • Return the same pending promise to all callers until completion.
  • Reflect loading state in the UI and disable controls while pending.
  • Use cancellation for search and navigation flows where stale requests are common.

Course illustration
Course illustration

All Rights Reserved.