Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How can I keep one failed request from leaving a loading indicator stuck?

Quick answer

Put the loading-state reset in a finally block so it runs whether the request succeeds or rejects. Handle the error separately so the user gets useful feedback. If you offer a retry, keep the error and retry option visible, but end the loading state when that attempt has settled; start loading again when a new attempt begins.

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

My page sets loading to true before an asynchronous request and resets it after the request succeeds. When the request fails, the spinner stays on. What should I change?

Geez

Put the reset in a finally block rather than only after the success path. A promise’s finally() callback runs after it settles, whether it fulfills or rejects, making it suitable for cleanup such as resetting a loading indicator [S1].

For example, in a React-style component:

async function loadItems() {
  setLoading(true);
  setError(null);

  try {
    const items = await fetchItems();
    setItems(items);
  } catch (error) {
    setError("Could not load items. Please try again.");
  } finally {
    setLoading(false);
  }
}

The catch handles the failed attempt; finally clears the loading state after either outcome. Adapt the setters and error message to your app. This pattern does not establish why a particular request failed or guarantee that it will succeed.

Follow-up question

Why not just add setLoading(false) inside catch?

Geez

You can reset it in both the success and failure paths, but that duplicates cleanup and makes it easier to miss a path later. finally puts the shared reset in one place. Keep it focused on cleanup: if the request rejects, handle that rejection in catch or pass it to a caller that handles it. A throw or rejected promise inside finally can change the final outcome, so avoid putting unrelated error-prone work there [S1].

A useful separation is: try for the request and success updates, catch for error handling, and finally for ending this attempt’s loading state.

Follow-up question

I want the retry button to remain available after a failure. Should I leave loading true until the user retries?

Geez

Usually, represent two different facts with separate state: whether a request is currently in progress, and whether the user can retry. Once an attempt has failed, set loading to false and show an error and retry button. When the user starts another attempt, set loading to true again. Keeping a spinner active while no request is running can make the page appear stuck rather than ready for a retry.

After the failure in the example, render the retry control when there is an error and loading is false. Its click handler can call loadItems() again. Whether to retain old results, clear them, or show a different message is an application-specific design choice; promise cleanup does not prescribe that behavior [S1].

Follow-up question

What should I check if the indicator still stays on after adding finally?

Geez

First, confirm that every path which turns loading on reaches the try and its finally. Put the request call inside the try, as in the example, so a synchronous throw from that call also reaches the cleanup. Next, check that the indicator reads the same loading state you reset, and that no later code sets it back to true. If your framework batches updates or the component can be removed during the request, inspect that behavior separately; the promise cleanup reference does not establish how your interface handles those cases.

Use this quick review checklist:

  1. Set loading to true when an attempt starts.
  2. Put the awaited request inside try.
  3. Handle or deliberately propagate failure in catch.
  4. Reset loading in finally.
  5. Show retry as a separate action, not as a permanently active loading state.

A practical checklist

  • Set loading to true at the start of each request attempt.

  • Place the awaited request inside the try block.

  • Handle the failure in catch, or ensure a caller handles it.

  • Reset loading in finally so success and rejection both reach cleanup.

  • Show retry availability separately from whether a request is in progress.

A few more useful answers

Does `finally` turn a failed request into a successful one?

No. It runs cleanup after settlement; it does not make a rejected request succeed. Handle or propagate the rejection separately.

Can I put the error message update in `finally`?

Usually, keep outcome-specific messages in catch and shared cleanup in finally. Code that throws or rejects inside finally can alter the resulting promise outcome [S1].

Sources

References used for this guide.

  1. [S1] Promise.prototype.finally() — JavaScript | MDN
    developer.mozilla.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work