Direct Answer
To ensure DataTables displays only the most recent server-side response:
- Abort the previous jqXHR before each new draw by storing the jqXHR returned from the
ajax option (or captured in the preXhr event) and calling .abort() on it. - Require the server to echo the
draw parameter exactly as received; DataTables (current versions) automatically discards responses whose draw value does not match the expected draw counter. - Set a per-instance timeout by passing
timeout inside the ajax configuration object (e.g., ajax: { url: '/data', timeout: 30000 }) instead of relying on global jQuery.ajaxSetup.
Why This Works
Draw Parameter as Sequence Guard
DataTables sends an incrementing integer draw with every server-side request. In modern versions (1.10.x and 2.x), the client-side library compares the draw value returned in the JSON response against the draw it expects for that table instance. If they differ, the response is ignored and the table does not update. This protection only works when the server reads the incoming draw and writes it back unchanged.
Aborting Prior Requests
DataTables does not automatically cancel in-flight requests when a new draw is triggered (e.g., rapid paging, search keystrokes). Developers must keep a reference to the last jqXHR and abort it:
let lastXhr = null;
$('#example').DataTable({
serverSide: true,
ajax: function (data, callback, settings) {
if (lastXhr) { lastXhr.abort(); }
lastXhr = $.ajax({
url: '/api/data',
data: data,
dataType: 'json',
timeout: 30000, // per-instance timeout
success: callback,
error: function (jqXHR, textStatus) {
if (textStatus !== 'abort') { /* handle real errors */ }
}
});
}
});
The error handler must ignore textStatus === 'abort' to avoid spurious error UI.
Per-Instance Timeout Without Global Side Effects
Passing timeout inside the ajax object (or the $.ajax call in a custom function) scopes the timeout to that table only. Global jQuery.ajaxSetup({ timeout: ... }) affects every Ajax call on the page and should be avoided unless intentionally shared.
Alternative: Client-Side Draw Guard
If you cannot modify the server to echo draw, implement a local guard in a custom ajax function:
let currentDraw = 0;
ajax: function (data, callback, settings) {
const thisDraw = ++currentDraw;
$.ajax({
url: '/api/data',
data: data,
success: function (resp) {
if (resp.draw === thisDraw) { callback(resp); } // ignore stale
}
});
}
Server-Side Responsibilities
- Read the
draw query parameter (integer) and return it verbatim in the JSON response. - Ensure endpoints are read-only or idempotent; aborting the client connection does not guarantee the server stops processing.
- Handle client disconnects gracefully (e.g., cancel long-running queries) to avoid wasted resources.
Verification Steps
- Open the browser Network panel, trigger rapid consecutive draws (fast paging or typing in search), and confirm earlier requests show as cancelled/aborted while only the final response renders.
- Temporarily add a server-side delay (e.g.,
sleep(5)) for an early draw and verify the table does not update when that delayed response finally arrives. - Log the
draw value sent and echoed back to confirm the server returns the identical integer. - Set
ajax: { timeout: 5000 } on one table, hit a slow endpoint, and verify only that table's requests time out while other page Ajax calls remain unaffected.
Version Assumption & Diagnostic Needed
The exact behavior of draw-mismatch rejection and event names (preXhr vs xhr) varies between DataTables 1.10.x and 2.x. Please confirm which major version you are using so the recommended event hooks and abort pattern can be precise.