A trading dashboard requests an instrument preview. Before the response arrives, the user selects another instrument. Both requests succeed. The older one finishes last and replaces the newer view.
Nothing failed at the transport layer. The bug is accepting a result that no longer belongs to the current selection.
This article uses a small synthetic JavaScript fixture. It is a UI acceptance example, not a broker integration or an order-routing system.
Give each selection its own identity
Comparing only the instrument name is insufficient. Consider ES, then NQ, then ES again. A response from the first ES request must not become valid merely because the screen says ES again.
For one in-memory JavaScript session, a fresh object can serve as the request ticket:
function session() {
let current = null;
let view = null;
return {
select(instrument) {
current = { instrument };
view = null;
return current;
},
accept(ticket, result) {
if (ticket !== current) return false;
view = { instrument: ticket.instrument, result };
return true;
},
read: () => view,
};
}
The ticket is created when the selection changes, captured by the request, and checked immediately before committing the result. The server does not need to know this local object.
const s = session();
const first = s.select('ES');
s.select('NQ');
const latest = s.select('ES');
console.assert(s.accept(first, 'stale') === false);
console.assert(s.read() === null);
console.assert(s.accept(latest, 'fresh') === true);
No timer or live service is required to exercise the failure. Deliver responses in the wrong order on purpose. That makes the acceptance rule reproducible instead of hoping a slow network reveals it.
Four checks, including the same-symbol case
| Sequence | Expected result |
|---|---|
| Select ES, complete its current request | ES result appears |
| Select ES then NQ, complete NQ then ES | NQ remains visible |
| Select ES, NQ, ES; complete the first ES request | Old result is rejected |
| Display ES, then select NQ before completion | Old result is cleared |
I ran these four cases with Node's built-in test runner. All four passed. They test this small in-memory acceptance function only.
Cancellation and acceptance do different jobs
Cancelling obsolete work can save resources. A separate acceptance check answers whether a completion may change the current view. Apply the same ownership check to success, error and loading-state updates; an old error should not replace a newer successful result either.
Object identity is deliberately local. A process boundary, persisted request or reload needs an explicit request identifier and a defined lifetime. This fixture also does not validate the payload, prove data freshness, handle authorization or prevent duplicate broker actions.
What to put in a repair brief
“The wrong symbol sometimes appears” is difficult to reproduce. Specify the selection sequence, completion order and exact view expected after each completion. Include the switch-away-and-back case. That one distinguishes selection identity from a coincidentally matching symbol name.
The useful boundary is simple: a successful response is allowed to update the screen only while its request still owns that screen state.
Top comments (0)