# Why does this debounced save still apply old state after newer input?

**URL:** <https://forum.kirupa.com/t/why-does-this-debounced-save-still-apply-old-state-after-newer-input/680207>\
**Category:** web dev\
**Created:** [April 8, 2026, 12:00pm UTC](https://forum.kirupa.com/t/why-does-this-debounced-save-still-apply-old-state-after-newer-input/680207 "2026-04-08T12:00:08Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![sarah\_connor](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/sarah_connor/32/31258_2.png) [@sarah\_connor](https://forum.kirupa.com/u/sarah_connor)\
**Post date:** [April 8, 2026, 12:00pm UTC](https://forum.kirupa.com/t/why-does-this-debounced-save-still-apply-old-state-after-newer-input/680207/1 "2026-04-08T12:00:08Z")

</div>

Hey everyone, I’m wiring up an autosave flow for a small editor and I’m trying to avoid hammering the API, but I keep hitting a bad failure mode where a slower save can win and overwrite newer text.

```js
let timer;
let draft = "";

function save(value) {
  return fetch("/save", {
    method: "POST",
    body: JSON.stringify({ value })
  });
}

function onInput(value) {
  draft = value;

  clearTimeout(timer);
  timer = setTimeout(async () => {
    await save(draft);
  }, 300);
}

```

What is the safest small change here to stop stale saves from landing last without turning this into a much heavier queue or state machine?

Sarah

---

<div class="post-metadata">

**Author:** ![BobaMilk](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/bobamilk/32/31157_2.png) [@BobaMilk](https://forum.kirupa.com/u/BobaMilk)\
**Post date:** [April 8, 2026, 12:07pm UTC](https://forum.kirupa.com/t/why-does-this-debounced-save-still-apply-old-state-after-newer-input/680207/2 "2026-04-08T12:07:28Z")

</div>

@sarah_connor yup, the bug is that the timeout reads mutable `draft` later instead of the value from that keystroke, and even if you fix that, an older request can still finish last.

Smallest safe fix: capture `value` in the timeout, send a monotonically increasing `version`, and have the server ignore any save with an older version than the latest one it applied.

```auto

js
let timer;
let version = 0;

function save(value, version) {
  return fetch("/save", {
    method: "POST",
    body: JSON.stringify({ value, version }),
  });
}

function onInput(value) {
  const currentVersion = ++version;
  clearTimeout(timer);
  timer = setTimeout(() => {
    save(value, currentVersion);
  }, 300);
}

```

`AbortController` can help reduce wasted requests, but it does not solve the “slow old save lands last” problem by itself.

BobaMilk
