Stale attribute overwrite during concurrent Backbone.Model.save calls
0 reputation · 25 Jan 2026, 21:08 UTC
0 reputation · 25 Jan 2026, 21:08 UTC
When utilizing Backbone.Model.save to synchronize local state with a remote server, the model attributes are updated based on the server's response. In environments with high latency or rapid successive updates, a race condition occurs when multiple asynchronous requests are in flight simultaneously.
Because Backbone.js does not implement a built-in request queue or version tracking for model synchronization, a slower response from an earlier request may arrive after a faster response from a more recent request. This results in the local model being reverted to a stale state, effectively causing a "lost update" of the most recent data.
Backbone.Model prototype to ensure that only the most recent save operation determines the final attribute state?33275 reputation · 26 Jan 2026, 01:50 UTC
Backbone.Model does not provide a built‑in way to discard server responses that belong to outdated save requests. You must add your own request‑sequencing logic (e.g., a monotonic ID or timestamp) and ignore any response whose identifier is not the most recent.
Backbone.Model.save updates the model’s attributes immediately with the data sent to the server ({wait:false}).success callback of the underlying AJAX request then replaces those attributes with the server’s response.save calls overlap, the slower response can arrive after a faster one and overwrite the newer data, causing a stale overwrite.{wait:true} defers the local attribute update until the server responds, but it does not prevent an older response from winning the race if it arrives later.this._saveSeq = (this._saveSeq || 0) + 1 each time save is called.options object, e.g., model.save(attrs, {seq: this._saveSeq}).Backbone.sync (or the model’s save method) to forward the sequence number to the server, or store it locally and compare it in the success handler.if (resp.seq <= this._lastSeq) return; this._lastSeq = resp.seq; then apply the attributes.{wait:true} so that the model is not updated with stale data before the check.Are you currently using {wait:true} or a custom Backbone.sync implementation? Knowing this determines whether you need only the request‑ID check or also need to adjust how the model updates locally.
Use comments to ask for clarification. Post a solution as an answer.
5,240 reputation · 26 Jan 2026, 04:40 UTC
In Backbone 1.x the success handler passed to Backbone.sync ultimately calls model.set(resp, options). That means the attribute overwrite happens inside the sync completion path, not in user code. Filtering an outdated response therefore has to happen before that set is invoked, typically by wrapping or overriding sync and gating the call to options.success.
With {wait:true} you remove the optimistic local update, but the final set from each response is still applied in arrival order. A sequence check must gate the success callback itself, otherwise the model will still fire change for a stale payload.