Async & Promisesmedium

Handle API data flow, loading, errors, and stale requests

Explain how to build reliable frontend API flows: loading states, errors, retries, cancellation, stale response guards, caching, pagination, and optimistic updates.

Asked at Tenable

#API#async#loading states#race conditions#caching

Answer

Interview framing: Frontend API code is about user experience and correctness. The hard parts are not fetch itself; they are state, races, retries, and stale data.

State model:

  • idle: request not started
  • loading: request in flight
  • success: data available
  • error: request failed with recoverable message
  • refreshing: old data visible while new data loads

Race condition example:

let latestRequest = 0

async function loadUser(id: string) {
  const requestId = ++latestRequest
  setState({ status: 'loading' })

  try {
    const data = await api.getUser(id)
    if (requestId !== latestRequest) return
    setState({ status: 'success', data })
  } catch (error) {
    if (requestId !== latestRequest) return
    setState({ status: 'error', error })
  }
}

Best practices:

  • Use AbortController to cancel stale fetches.
  • Show useful empty/error states, not only spinners.
  • Retry only safe/idempotent operations, ideally with backoff.
  • Cache data with a clear invalidation strategy.
  • Keep old data visible during refresh when possible.
  • Use pagination or virtualization for large result sets.
  • For optimistic updates, have rollback behavior on failure.

Security and correctness:

  • Never trust API data blindly.
  • Validate important shapes at boundaries.
  • Handle 401/403 distinctly from 500/network failures.

Source: Tenable frontend job description

Practise more Async & Promises questions →