Long polling ajax requests with DataTables

Long polling ajax requests with DataTables

dcpdcp Posts: 5Questions: 3Answers: 0

Hello folks,

I can't quite get around this idea and if someone could propose a solution it will be great.

Currently I have an restful API, which works great with DataTables.
The problem I currently have, I want to expose into the API some operations, which might be time consuming. So instead of making an AJAX request and waiting for the request to complete, I was thinking something like:

  1. Send an Ajax request and return "job handle" (for example: jobX)
  2. Process the jobX into the background, using Gearman or something different.
  3. DataTables should ask for the job status each second for example, and once the job has completed just to draw the table.

So ideal would be into my initialisation:

"sAjaxSource": "/api/long_taking_oepration",

and somehow to make DataTables automatically check "/api/job_status?job=jobX"?

Any thoughts and advices?

Thanks!

Answers

  • allanallan Posts: 63,687Questions: 1Answers: 10,500 Site admin

    Your stumbling point will be point 3. DataTables knows nothing about your external API, and shouldn't (it would be different for every implementation!). What I would suggest is that you provide the code that checks the job queue to see if jobs are done and if so then have it use the DataTables API to add the rows (rows.add().).

    Regards,
    Allan

  • dcpdcp Posts: 5Questions: 3Answers: 0

    I was thinking more likely hooking into some of the callbacks in order to query the API and check the job if it's finished.
    My first try of the implementation is:

        "fnServerData": function ( sSource, aoData, fnCallback, oSettings ) {
    
         (function poll(){
             var timer = setTimeout(function(){
                var ajax = $.ajax({ 
                  url: sSource,
                  success: function(data){
                      if ("poll has completed") {
                        fnCallback(data);
                      }
                      else {
                        //Setup the next poll recursively
                        poll();
                      }
                  }, 
                  dataType: "json"
                });
             }, 1000);
          })(); 
     }
    

    however I am not sure if that's the correct place to hook and implement something like that. Any thoughts?

  • allanallan Posts: 63,687Questions: 1Answers: 10,500 Site admin

    I would suggest probably not there. Are you planning on using server-side processing or client-side? That is certainly going to be the key question. For client-side, you probably would get away with that. For server-side, no (I'm not sure how this would be done with server-side processing atm to be honest).

    I think just running your poll and then using the API to add rows would probably be the best way. But I'm sure there are many ways to do it!

    Allan

  • pingcrosbypingcrosby Posts: 29Questions: 4Answers: 1

    I use Datatables in combination with SignalR to do just this thing. The table loads and then SignalR pushes the update when complete. If you can use a framework such as SignalR then its trivial to just create a handler that inserts rows. (this is my preferred method).

    In another implementation I also use a standard javascript timeout to just poll the server. As soon as the table loads a timer kicks off and just looks for updates from the server ever 10 seconds or so.

    This is roughly how i achieved this using standard a timeout poll.

    Hope this may prove useful.

    // intialise the table - as soon as its ready start the timer ..
    
         submissionTable= $('#submissionTable').DataTable({
                initComplete: StartTimer,       // when fully drawn start the interval timer (this basically calls an full ajax redraw)
    
                ajax: {
                    url: webApi + summaryId,
                    headers: { __RequestVerificationToken: token }
                },
    
       function StopTimer() {
            clearTimeout(intervalId);   // stop when we ajax and when we expand the master detail
            remaining = countdownTime;
        }
       
        function StartTimer()
        {
    // recursively call myself - resetting each time when complete
            if (remaining === 0) {
                reloadchanges();  // UPDATE THE TABLE
                return; // dont start another timer.. the ajax call itself will control this
            }
    
    // we display a timer on screen that ticks down 5,4,3,2,1 etc
            document.getElementById('countdown').innerHTML = remaining;
            intervalId = setTimeout(function () { StartTimer(remaining--); }, 1000);
        }
    
    // this is the full table update
    
      function reloadchanges() {
            StopTimer(); // allow the refresh event to continue -- without another timer firing
            var statuses = [];
    
            // get all the table rows statuses and id jsonify and send to server
            // server will compare these client side states and send back a new
            // record set of any changes
            submissionTable.rows().eq(0).each(function (index)
            {
                var row = submissionTable.row(index);
                var d = row.data();    // ... do something with data(), or row.node(), etc
    
                statuses.push({ ID : d.ID, Status : d.Status });
            });
            var jsonobj = JSON.stringify({ SummaryID: summaryId, Details: statuses });
    
            try {
                // im using MVC and i want this token.
                // lift the antiforgery token as slip it into the header - this could throw !
                var token = $('input[name=__RequestVerificationToken]').val();
      
                $('.ajax-progress').show();  // show a spinner..
    
                $.ajax({
                    url: webApi + "Refresh",
                    data: jsonobj, 
                    type: "post",
                    contentType: "application/json",
                    headers: { __RequestVerificationToken: token },
                    selfTable: submissionTable,  // store the table across the callback
                    success: function (respArray, textStatus, jqXHR)
                    {
                        if (jqXHR.status == 204 || !respArray) {
                            // server returns a 204 (NOCONTENT) if nothing todo.
                            return; // just grabbed by the complete handler.
                        }                                             
                        // to update i am doing something extremely lazy and just walkkingt the dataset
                        // for big datasets this would be unreasonable but for small datasets like this 
                        // i dont think its an issue - we only ever expect ~50 records 
                        var table = this.selfTable;  // get table from parent context
                        table.rows().eq(0).each(function (index) {
                            var row = table.row(index); // containing row for data
                            var data = row.data();      // get data for this row
    
                            for (var i = 0; i < respArray.length; i++)
                            {
                                // loop through all the response changes 
                                var resp = respArray[i];
                                if (resp.ID == data.ID)
                                {                                    
                                    row.data(resp);      // we got a match  -- update the data row
                                    var tr = row.node(); // this can be null - eg if ..deferred rendering set
                                    if (tr) {
                                        // generate the clickable link icon 
                                        generateEditDeleteLink(tr, resp);                       
                                        // grey out / disable the row if deleted
                                        disableRow(tr, resp.Status);
                                    }
                                }
                            }
                        });                
                    }, // success
                    error: function(jqXHR, textStatus, errorThrown )
                    {
                        alert(jqXHR.responseText);
                    },
                    complete: function (response) {                  
                        // no matter what happens -- error or success do this
                        $('.ajax-progress').hide();
                        StartTimer(); // allow the refresh event to continue
                    }
                }); // ajax call
            }
            catch(e) {
                $('.ajax-progress').hide();
                StartTimer(); // allow the refresh event to continue             
            }
        }
    
  • allanallan Posts: 63,687Questions: 1Answers: 10,500 Site admin

    Nice one - thanks for sharing your code with us!

    Allan

This discussion has been closed.