How can I stop all the currently ongoing Ajax queries that DataTables instance have started?
Asked Answered
C

3

7

Brief Description

Resetting a test server to a known state causes my tests to fail due to Ajax requests launched by DataTables instances being ongoing at the time the server is reset. I'd like to prevent this by stopping the DataTables requests before the server is reset.

Detailed Description

I have an application in which I use DataTables on some pages. These DataTables all perform server-side queries to populate their tables.

When I perform system testing, sometimes there is a race condition that can happen:

  1. The test runner starts the test server.

  2. The test runner loads in a test browser a page with a DataTable instance somewhere on it.

  3. The test runner runs the test, which performs its checks and ends.

  4. The test runner resets the test server to a known state for the next test.

  5. An alert shows up on the page, saying that DataTables experienced an Ajax error. The alert says:

DataTables warning: table id=[some id] - Ajax error. For more information about this error, please see http://datatables.net/tn/7

  1. My testing system, which is not expecting the alert, is confused and registers a failure even though the test was in fact successful. (Or in some cases, it crashes.)

I know this happens because the server abruptly interrupted an Ajax request. What I am looking for is a way to prevent the alert from coming up in the first place. I'd like to stop all ongoing DataTables requests before the server is reset.

Solutions already rejected

  • Tell the DataTables instances not to use alerts: I want my tests to fail louldly if a DataTables instance runs into an issue that is not related to resetting the test server.

  • Modifying the test server: I prefer to keep the server simple and not worry there about requests that may go unanswered.

  • Wait client-side for all requests to be over: this can slow down tests considerably, especially when this wait is repeated for dozens of tests.

  • Direct the test browser to a new page, without DataTables on it, as this will interrupt the current requests: again this will hurt test performance.

Comenius answered 23/10, 2014 at 15:41 Comment(0)
C
13

Solution

Have the software that drives the browser execute the following code in the browser after a test has completed all its checks. (This would be in some sort of "tear down" code run after the test.)

if (typeof $ !== "undefined" && $.fn.dataTable) {
    var all_settings = $($.fn.dataTable.tables()).DataTable().settings();
    for (var i = 0, settings; (settings = all_settings[i]); ++i) {
        if (settings.jqXHR)
            settings.jqXHR.abort();
    }
}

Explanation

The code is written to work even when executed on pages that don't have jQuery loaded or don't have DataTables loaded. So it first checks whether they are loaded, and does not do anything if they are not loaded. Then it fetches the settings objects for all DataTable instances. In each settings objects, it checks for the presence of jqXHR, which is populated with a jQuery jqXHR object when an Ajax request has been made. It will call the abort() method on it, thus aborting the request.

The code above works with DataTables 1.10 whether the tables use the 1.10 API or the 1.9 API. However, note that the jqXHR field is not formally part of the public API. At the same time, one of the devs speaks about it without caveat on DataTables forum, so this is probably not the riskiest part of the private API to rely on. And a solution that relies purely on the public API would be much more cumbersome as all DataTable instances would have to be modified to track the events that mark the start of an Ajax transaction and its end or have customized Ajax handlers, etc. This would have to be done not only for the code proper to the project being tested but any 3rd party library that provides HTML widgets that happen to use DataTables.

Note that the code above does not prevent DataTables instances from initiating new requests. But this is not a concern I have.

Comenius answered 23/10, 2014 at 15:41 Comment(0)
X
5

I think a better way is use the preXhr.dt event in DataTables.

$('.datatable').
  on('preXhr.dt', function ( e, settings, data ) {
    if (settings.jqXHR) settings.jqXHR.abort();
}).DataTables({});
Xylography answered 6/1, 2017 at 16:50 Comment(5)
I don't think so. I see that preXhr is emitted "prior to DataTables making an Ajax request to the server for data". (Emphasis in original.) If I use the code you show when I initially set up my tables, then I'm pre-aborting all Ajax requests and my tests will fail because my tables don't work. If I add the listener once I'm done with my tests, then the problem is that I'm only aborting future requests. There are requests that were generated during testing that are still not resolved by the time the test is over. I need to abort these to avoid the error I'm reporting.Comenius
@Comenius actually the above code will work. It only aborts ajax requests if another one is already running. So if the ajax request fires once for the page load, it doesn't "stop" it from working. If say 5 ajax requests are made in a row, the first 4 are cancelled (using .abort()) and the 5th one runs and returns the data. I've tried this in an application and monitored it with the browser console's network tab.Visional
@Visional In order for things to work properly, the 4 initial Ajax requests should not be aborted --- so that the table is actually testable --- but the 5th one should be aborted so that any trailing Ajax request that runs after the test is over is aborted from the client side, instead of erroring out because the server abruptly closed the connection when it reset. You describe the exact opposite of this desired behavior.Comenius
Of course the initial ones should be aborted. Anything which was started earlier than the latest request is essentially useless because it's generating data which doesn't match the search criteria! My application sends the requests on a .keyup event attached to a form. If the user types "abcde", you do not want 4 ajax requests which have sent "a", "ab", "abc", "abcd" - because those will not yield the correct results for the user. Anything which is older than the latest request should be aborted. I've got this working on an application I'm developing at the moment.Visional
For serverside and filtering this works like a charm. Good solution and not so hacky like the marked answer. But I would like to see curly braces in the if - like this: if (settings.jqXHR) { settings.jqXHR.abort(); }Acrylonitrile
L
1

I like the chosen solution but it didn't work when I was loading Datatable grids in injected html requests. I used the on Search event to trigger the code proposed above and it seems to work fine.

myDataTable.on('search', function () {
            console.log ('Searching aborted');
            //fix to prevent datatables from crashing searches on large data sets
            if (typeof $ !== "undefined" && $.fn.dataTable) {
                let allSettings = $($.fn.dataTable.tables()).DataTable().settings();
                for (let i = 0, settings; (settings = allSettings[i]); ++i) {
                    if (settings.jqXHR)
                        settings.jqXHR.abort();
                }
            }
        });
Lananna answered 14/7, 2022 at 5:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.