Hone logo
Hone
Problems

Robust API Call with Exponential Backoff

Network requests can be unreliable. Implementing retry logic with exponential backoff is a crucial technique for building resilient applications that can gracefully handle temporary failures like network glitches or server overload. This challenge asks you to create a Javascript function that attempts to execute a provided function (representing an API call or other potentially failing operation) with retry attempts and increasing delays.

Problem Description

You need to implement a retryWithBackoff function that takes a function fn as an argument. This fn represents the operation you want to retry (e.g., an API call). The retryWithBackoff function should attempt to execute fn up to a specified maximum number of times. If fn throws an error, the function should wait for a duration determined by an exponential backoff strategy before retrying.

Key Requirements:

  • Retry Attempts: The function should retry the provided function a configurable number of times.
  • Exponential Backoff: The delay between retries should increase exponentially. Start with a base delay (e.g., 100ms) and double it for each subsequent retry.
  • Maximum Delay: Implement a maximum delay to prevent excessively long waits.
  • Error Handling: If the function fails after the maximum number of retries, the last error should be thrown.
  • Success: If the function succeeds on any attempt, the result should be returned immediately.

Expected Behavior:

The retryWithBackoff function should return the result of the successful execution of fn. If fn throws an error after all retries are exhausted, the last error thrown should be re-thrown by retryWithBackoff.

Edge Cases to Consider:

  • fn immediately throws an error on the first attempt.
  • fn succeeds on the first attempt.
  • fn fails multiple times before eventually succeeding.
  • Maximum number of retries is reached without success.
  • Base delay and maximum delay are zero or negative (handle gracefully, perhaps by defaulting to reasonable values).

Examples

Example 1:

Input:
const apiCall = () => { throw new Error("Network error"); };
retryWithBackoff(apiCall, 3, 100, 2000);

Output:
Error: Network error (thrown after 3 retries)

Explanation:
The apiCall function always throws an error. The retryWithBackoff function attempts it 3 times with increasing delays (100ms, 200ms, 400ms). Since it always fails, the last error is re-thrown.

Example 2:

Input:
const apiCall = (attempt) => {
  if (attempt === 1) {
    throw new Error("Network error");
  }
  return "Success!";
};
retryWithBackoff(apiCall, 3, 100, 2000);

Output:
"Success!"

Explanation:
The apiCall function throws an error on the first attempt. The retryWithBackoff function retries with delays of 100ms, 200ms, and 400ms. On the second attempt, apiCall returns "Success!", which is then returned by retryWithBackoff.

Example 3: (Edge Case - Immediate Success)

Input:
const apiCall = () => "Success!";
retryWithBackoff(apiCall, 3, 100, 2000);

Output:
"Success!"

Explanation:
The apiCall function succeeds immediately. The retryWithBackoff function returns the result "Success!" without attempting any retries.

Constraints

  • fn must be a function that takes no arguments and returns a value or throws an error.
  • maxRetries must be a positive integer.
  • baseDelay must be a non-negative integer representing the initial delay in milliseconds.
  • maxDelay must be a non-negative integer representing the maximum delay in milliseconds.
  • The exponential backoff should double the delay for each retry, but not exceed maxDelay.
  • The function should be reasonably performant; avoid unnecessary computations.

Notes

  • Consider using setTimeout to implement the delays.
  • Think about how to handle the error thrown by fn and ensure it's re-thrown correctly after all retries have been exhausted.
  • The exponential backoff formula can be expressed as delay = baseDelay * 2^attemptNumber. Ensure this is capped by maxDelay.
  • The attempt parameter in the examples is for illustrative purposes; your function should manage the retry attempt count internally.
  • Focus on clarity and readability in your code. Good error handling and comments are appreciated.
Loading editor...
javascript