In AngularJS, retrying failed HTTP requests automatically is a common requirement, especially when dealing with intermittent network issues or server-side errors. For this purpose, you can use $httpInterceptor
in combination with AngularJS’s $q
service to implement automatic retries. By setting up an HTTP interceptor, you can monitor request failures and retry them a specific number of times before failing definitively.
1. Why Automatically Retry Failed HTTP Requests?
Automatic retries are useful in situations where:
- Network conditions are unstable: Sometimes requests fail due to temporary network issues.
- Server-side delays or throttling: If the server is temporarily unavailable or experiencing high load.
- Intermittent errors: A retry strategy can mitigate issues like rate limiting, temporary server errors, or transient connectivity issues.
2. Implementing Automatic Retries
To retry HTTP requests, you need to intercept failed requests using $httpInterceptor
and define a retry strategy based on the HTTP status code, network condition, or any custom condition.
3. Steps to Implement Automatic Retries
Step 1: Create the Retry Logic Interceptor
First, you need to create an interceptor that will catch the failed HTTP requests and retry them.
angular.module('myApp', [])
.factory('retryInterceptor', function($q, $timeout) {
// The maximum number of retry attempts
const MAX_RETRIES = 3;
// The delay (in ms) between retries
const RETRY_DELAY = 1000;
// Keep track of the number of retries
function retryRequest(retryCount, config) {
if (retryCount >= MAX_RETRIES) {
return $q.reject(config);
}
// Increment the retry count
retryCount++;
// Retry the request after a delay
return $timeout(function() {
config.retryCount = retryCount;
return $http(config);
}, RETRY_DELAY);
}
return {
// Request interceptor (for adding retryCount)
request: function(config) {
config.retryCount = 0; // Start with 0 retry count
return config;
},
// Response interceptor (to handle retries)
responseError: function(rejection) {
const config = rejection.config;
const retryCount = config.retryCount || 0;
// Retry logic: Only retry for certain HTTP status codes, e.g., 500 or 503
if (retryCount < MAX_RETRIES && (rejection.status === 500 || rejection.status === 503)) {
return retryRequest(retryCount, config);
}
// Reject if max retries exceeded or status code isn't appropriate
return $q.reject(rejection);
}
};
});
Step 2: Register the Interceptor
Once the retry logic is implemented, you need to register the retryInterceptor
globally within your AngularJS application.
angular.module('myApp', [])
.config(function($httpProvider) {
// Register the interceptor globally
$httpProvider.interceptors.push('retryInterceptor');
});
Step 3: Example Usage
Now, every HTTP request made by your AngularJS application will be subject to this retry logic. For example, you can use it to call an API:
angular.module('myApp')
.controller('ApiController', function($scope, $http) {
$scope.fetchData = function() {
$http.get('/api/data')
.then(function(response) {
// Handle successful response
console.log('Data fetched:', response.data);
})
.catch(function(error) {
// Handle the final error after retries
console.error('Failed to fetch data:', error);
});
};
});
In the above example, the request will automatically retry up to 3 times if it fails with a 500 or 503 error code. The retry attempt will be delayed by 1 second (RETRY_DELAY
), and after 3 retries, if the request still fails, it will be rejected, and an error will be logged.
4. Customizing Retry Logic
You can further customize the retry logic based on your application’s needs:
- Retry on specific HTTP status codes: You can retry on a specific set of status codes (e.g., 500, 503, or 408).
- Exponential backoff: Instead of using a fixed delay between retries, you can implement an exponential backoff strategy where the delay increases with each retry.
- Custom retry conditions: You can also check other conditions before retrying, such as the number of requests sent, the type of request (GET, POST), or network conditions.
Here’s an example of exponential backoff:
angular.module('myApp', [])
.factory('retryInterceptor', function($q, $timeout) {
const MAX_RETRIES = 5;
const INITIAL_DELAY = 1000; // 1 second delay
function retryRequest(retryCount, config) {
if (retryCount >= MAX_RETRIES) {
return $q.reject(config);
}
// Exponential backoff
const delay = INITIAL_DELAY * Math.pow(2, retryCount);
return $timeout(function() {
config.retryCount = retryCount;
return $http(config);
}, delay);
}
return {
request: function(config) {
config.retryCount = 0;
return config;
},
responseError: function(rejection) {
const config = rejection.config;
const retryCount = config.retryCount || 0;
if (retryCount < MAX_RETRIES && (rejection.status === 500 || rejection.status === 503)) {
return retryRequest(retryCount, config);
}
return $q.reject(rejection);
}
};
});
5. Advantages of Automatic Retries
- Improved User Experience: Automatic retries can provide a smoother user experience by minimizing interruptions caused by transient errors.
- Handling Temporary Server Issues: If a request fails due to temporary server issues (like 503 Service Unavailable), retrying the request can often succeed after a short delay.
- Network Resilience: In environments where the network can be unreliable (e.g., mobile applications), automatic retries improve reliability.
6. Disadvantages and Considerations
- Overloading the Server: If too many retries are made in a short time, it could overload the server or make the problem worse.
- Infinite Retries: If the retry logic isn’t properly managed, you could end up in an infinite retry loop. Always set a maximum retry limit.
- Exponential Backoff: If using exponential backoff, you need to ensure that delays don’t create a bad user experience due to long wait times.