Answering the Question
Backbone’s save() simply fires an AJAX request each time it is invoked. It does not keep a record of pending requests, nor does it automatically cancel or deduplicate concurrent calls. Therefore, two rapid save() invocations can result in two identical network requests, which may create duplicate records on the server if the first request eventually succeeds.
What Backbone Provides
- HTTP method selection:
POST for new models (no id), PUT for existing models (has id).
- No internal flag such as
isSyncing or a request queue.
- No built‑in cancellation of in‑flight XHRs.
Recommended Client‑Side Pattern
To guard against duplicate writes when you implement retry logic, add a lightweight flag or queue on the model instance. The pattern is:
- Set a flag before starting a sync:
model.isSyncing = true;
- Override
sync (or wrap the call) to check the flag: if model.isSyncing is true, either queue the request or simply return a resolved Promise that does nothing.
- Clear the flag in the
success and error callbacks: model.isSyncing = false;
- Implement retry logic only when
model.isSyncing is false: this guarantees that at most one request is in flight.
Example wrapper:
var syncWithGuard = function(method, model, options) {
if (model.isSyncing) {
// Optionally queue or ignore
return;
}
model.isSyncing = true;
var originalSuccess = options.success;
var originalError = options.error;
options.success = function(resp) {
model.isSyncing = false;
if (originalSuccess) originalSuccess(resp);
};
options.error = function(err) {
model.isSyncing = false;
if (originalError) originalError(err);
};
return Backbone.sync(method, model, options);
};
Backbone.sync = syncWithGuard; // Install globally or per‑model
With this guard, a second save() while the first is still pending will simply return without firing another request, preventing duplicate writes.
When to Rely on HTTP Idempotency
- If the model already has an
id, Backbone will use PUT. According to the HTTP spec, PUT is idempotent; repeating the same PUT will not create duplicate resources.
- If the server correctly implements
PUT semantics (e.g., ignoring duplicate payloads or using optimistic locking), you can rely on the HTTP layer to avoid duplication.
- For new models without an
id, POST is non‑idempotent, so the client‑side guard above is essential.
Practical Verification Steps
- Open the browser’s Network tab.
- Create a new model instance and call
model.save() twice in quick succession.
- Observe two separate XHRs:
POST if no id, PUT if id is present.
- After installing the guard, repeat the test and confirm only one XHR is sent.
Ask for a Missing Diagnostic Detail
To fine‑tune the recommendation, it would help to know whether your model has an id set before the first save() call. If it does, you can rely on PUT idempotency; if not, the client‑side guard becomes mandatory.