A preview can show the wrong file after two successful decodes. If a user selects one photo and quickly selects another, the first operation may finish last. An unconditional completion handler will then put the older photo back on screen.
The useful question at completion is whether this result still belongs to the current request. A counter can answer that without relying on which file ought to decode faster. Increment the counter whenever the user changes the selection, and capture its value before starting asynchronous work.
Give each selection an identity
Suppose two selections receive request numbers 1 and 2. Before request 1 updates the preview, it compares its number with the current value, 2. The mismatch tells it to discard its result and release any unused decoded resource. Request 2 owns the visible update.
A filename is a poor replacement for this request identity. A user can select the same file twice, and two files can share a name. Even the same file can be processed with different crop settings. Assign the identifier to the operation whose result you are waiting for.
Increment the counter when the selection is cleared as well. Otherwise, a decode that started before the clear action can put the old photo back into an intentionally empty preview. Changes to conversion settings may need the same treatment if they start a new asynchronous operation.
Check before changing shared state
Put the identity comparison immediately before assigning the preview or drawing onto the shared canvas. Checking only before an await leaves a gap: another selection can arrive while the operation is suspended. JavaScript resumes the earlier continuation later, after the current request has already changed.
Error messages and loading indicators need the same check. An old failure must not cover a newer successful preview. An old completion must not hide the spinner for work that is still current.
Keep cleanup separate from those interface decisions. If an unused result is an ImageBitmap, close it when the application no longer needs it. If the operation created an object URL, revoke it when its consumers have finished. Rejecting a stale result does not release resources by itself.
This does not cancel the decoder
The counter controls which result reaches the interface. It does not promise that the underlying work stops. A decode may continue using CPU time and memory even though its result will be discarded. Where an API supports cancellation, use its documented cancellation mechanism as a separate concern.
For expensive processing, consider limiting how many jobs can run at once. That changes scheduling; it does not remove the need to check request identity. A queued job can also become obsolete before it begins, so check its identity before starting expensive work and again before committing its output.
Test with deliberately reversed completion order. A small test helper can hold the first result until the second has completed, without needing particular image files or a slow machine. Confirm that clearing the selection stays clear, an older failure stays hidden, and every discarded decoded resource is cleaned up. The test should assert which selection the preview represents, not just that a preview exists.
Top comments (0)