You search for “phone”, then change the query to “laptop”. The laptop results appear first. A moment later, the older phone request finishes and replaces them.
Now the search field says laptop while the cards show phones. Both requests may have succeeded. The interface is still giving the wrong answer.
This is a useful example of why I need to think about timing in an application. I am describing a hypothetical listing search, not claiming that this bug was measured in my marketplace.
The newest request does not always finish last
A request started earlier can take longer than the one started after it. Network conditions, server work and caching can all affect the order.
So I cannot treat the last response received as the answer to the current search. I need to know which query and filters produced it.
I would include the search phrase, selected area, category and page in that identity. If somebody changes the area without changing the phrase, it is still a different search.
I separate typing from searching
The text in the field can change several times while somebody types. The product needs a clear rule for when a search begins.
A Search button gives an explicit action. A short debounce can reduce requests in a live-search interface by waiting for a pause in typing. These are different choices, and I want to choose according to the task.
Debouncing alone does not fix responses arriving out of order. A request already running can still complete after the next one.
Cancellation can save unnecessary work
For browser fetch requests, AbortController can signal that a request should be cancelled. MDN documents passing its signal to fetch and calling abort when the work is no longer wanted.
I also want the result handler to check whether the response belongs to the current search before showing it. That is useful when cancellation arrives late or when another part of the data flow cannot be cancelled in the same way.
Cancellation is not the same as rolling back a server operation. Here I am talking about reading search results. I should not apply the same assumption casually to payments or changes to stored records.
Old cards need context while loading
I can keep the previous results visible during a new request, or replace them with a loading view. If I keep them, the screen should make clear that they are previous results while the new search loads.
Otherwise somebody may open an old card thinking it matches the updated filters. A small loading indicator beside the search summary can explain the transition.
I would avoid clearing everything repeatedly while someone types. Too much movement can make it harder to compare or choose a result.
A failed search is not an empty search
“No matching listings” should mean a completed search returned no matches. A network error should say that the results could not be loaded.
The actions should fit the state. With no matches, a visitor might remove a filter or try another phrase. With a connection problem, retrying the same search may be the right next step.
I keep the query and filters in place after failure. Asking somebody to enter everything again adds work without solving the connection problem.
Changing a filter should reset the relevant page
If someone is on page five and chooses a category with only one page of results, leaving the old page number can produce a confusing empty view.
When the search identity changes, I would return to the starting page and make sure the displayed count, selected filters and cards agree. A result count from the previous search should not sit beside the new cards.
The test I would run
- Start one search with a deliberately slow response in a test setup.
- Start a different search that finishes sooner.
- Confirm the older response cannot replace the current results.
- Change area and category while a request is running.
- Test a successful empty result and a failed request separately.
I want search to answer what the visitor asked now. That means checking the query, the response and the state of the screen together, rather than celebrating that the endpoint returned data.
Reference: MDN: AbortController.
