Why does my async event pipeline sometimes apply stale state after a fast drag?

Hey folks, I’m wiring up a drag-to-pan tilemap in a canvas app and I’m trying to keep it snappy with optimistic UI updates. It mostly works, but if I drag fast and let go, I sometimes see the last few deltas apply out of order, and memory usage creeps up after a few minutes.

const pending = new Map();
let seq = 0;

canvas.addEventListener('pointermove', (e) => {
  if (!dragging) return;
  const id = ++seq;
  const delta = { dx: e.movementX, dy: e.movementY };
  applyLocal(delta); // optimistic
  pending.set(id, delta);

  sendDelta(delta).then(() => {
    // ack from server
    pending.delete(id);
  });
});

What’s a solid pattern to guarantee ordering and cleanup here (so late acks can’t reintroduce stale deltas, and pending doesn’t leak) without making the drag feel laggy?

Hari :slightly_smiling_face:

This usually happens when old promises from a previous drag are still allowed to touch current state.

Give each drag its own session id, attach that to every delta, and ignore any ack that doesn’t match the active session. Then clear pending on drag end and on a timeout so a dropped request doesn’t sit there forever.

Late acks should only clean up. They shouldn’t be able to re-apply anything.