Why does my async username check keep flipping the validity state?

Yo folks, I’m wiring up a signup form and I’m trying to validate a username as the user types, but I keep hitting a failure mode where slower responses overwrite newer ones and the field toggles between valid/invalid.

const input = document.querySelector('#username');
const status = document.querySelector('#status');
let controller;

input.addEventListener('input', async (e) => {
  const value = e.target.value.trim();
  if (!value) return;

  controller?.abort();
  controller = new AbortController();

  status.textContent = 'checking...';

  const res = await fetch(`/api/username?u=${encodeURIComponent(value)}`, {
    signal: controller.signal
  });
  const { available } = await res.json();

  input.setCustomValidity(available ? '' : 'Taken');
  status.textContent = available ? 'ok' : 'taken';
});

What’s the cleanest way to make this reliable so aborted/older requests can’t stomp newer validation, without making the UX laggy or the tests flaky?

1 Like