2

As a learning exercise I've hacked together a script for an SO feature request (for the purposes of this question please ignore the merits or otherwise of that request). In the script I've encountered a technical issue that my limited javascript knowledge can't get past and I'd appreciate suggestions on how to resolve it.

To avoid spamming the server I use some search hacks to determine the number of answers and accepted answers for a tag. This involves using window.setTimeout() to callback to a function that sends a get request for each tag, increasing the timeout on each call to stagger the requests.

To get the results in a single request involves appending &pagesize=1 to the end of the url in the get request, so that the number of pages in the results gives you the total number of results without having to make any further requests.

A side affect of this approach is that subsequent page views use &pagesize=1 and I only see a single entry. I attempt to resolve this by firing another query with &pagesize=30 to reset it afterwards, but as it is all asynchronous the timing of the last query can result in the pagesize either being 1 or 30, depending on which request completes first. I've tried adding a further timeout and callback for this "reset" query but it hasn't really helped.

Is there a means to monitor the queries, waiting until all have been completed, then once they have all completed send the reset request? Or is there another approach that I could take?

Community
  • 1
  • 1
Rich Seller
  • 83,208
  • 23
  • 172
  • 177

3 Answers3

5

You could make a call chain

Based on my previous idea of a ParallelAjaxExecuter, here's a SerialAjaxExecuter

$(function(){

  var se = new SerialAjaxExecuter( function( results )
  {
    console.log( results );
  }, 1000 );

  se.addRequest( $.get, 'test.php', {n:1}, function( d ){ console.log( '1 done', d ); }, 'text' );
  se.addRequest( $.get, 'test.php', {n:2}, function( d ){ console.log( '2 done', d ); }, 'text' );
  se.addRequest( $.get, 'test.php', {n:3}, function( d ){ console.log( '3 done', d ); }, 'text' );
  se.addRequest( $.get, 'test.php', {n:4}, function( d ){ console.log( '4 done', d ); }, 'text' );

  se.execute();

});

var SerialAjaxExecuter = function( onComplete, delay )
{
  this.requests = [];
  this.results  = [];
  this.delay    = delay || 1;
  this.onComplete = onComplete; 
}

SerialAjaxExecuter.prototype.addRequest = function( method, url, data, callback, format )
{
  var self = this;
  this.requests.push( {
      "method"    : method
    , "url"       : url
    , "data"      : data
    , "format"    : format
    , "callback"  : callback
  } );
  var numRequests = this.requests.length;
  if ( numRequests > 1 )
  {
    this.requests[numRequests-2].callback = function( nextRequest, completionCallback )
    {
      return function( data )
      {
        completionCallback( data );
        setTimeout( function(){ self.execute( nextRequest ); }, self.delay );
      }
    }( this.requests[numRequests-1], this.requests[numRequests-2].callback )
  }
}

SerialAjaxExecuter.prototype.execute = function( request )
{
  var self = this;
  if ( 'undefined' == typeof request )
  {
    request = this.requests[0];
    var lastRequest = this.requests[this.requests.length-1];
    lastRequest.callback = function( completionCallback )
    {
      return function( data  )
      {
          completionCallback( data )
          self.onComplete( self.results );
      }
    }( lastRequest.callback )
  }
  request.method( request.url, request.data, function( r )
  {
    return function( data )
    {
      self.results.push( data );
      r.callback( data );
    }
  }( request ) )
}

I didn't bake in a sleep period between requests, but that could certainly be added. Added the timeout

Note: this example is littered with console.log() calls for which you need firebug, or just remove them.

Community
  • 1
  • 1
Peter Bailey
  • 105,256
  • 31
  • 182
  • 206
  • works beautifully, just one question, in your answer you have the first request starting at 1, in my implementation I started at 0 and it worked fine. Should it make any difference? – Rich Seller Sep 11 '09 at 22:01
  • Makes no difference - those are just arbitrary labels. Internally it's handled as sort of linked-list type structure. Glad it works for you :D – Peter Bailey Sep 11 '09 at 22:06
0

I'm not sure if I fully understand the problem but why not chain the requests rather than using a setTimeout? So at the end of the response handler of one request fire off the next request.

slashnick
  • 26,167
  • 10
  • 55
  • 67
0

Append &pagesize= to every link on page that would need it with the pagesize you're currently using.

Pomyk
  • 3,288
  • 1
  • 17
  • 8
  • I'm specifically setting pagesize=1 to use it as a device to count the number of matches, if I use pagesize=30, I'll need to count the number in the last page to total up the matches, meaning two extra requests for each item in the list – Rich Seller Sep 11 '09 at 17:20
  • Maybe I don't understand... You add 'pagesize=1' to ajax queries and it "breaks" your normal browsing? – Pomyk Sep 11 '09 at 17:33
  • it's not an ajax query, just a jquery get request in a greasemonkey script. I've posted the source in the link on meta. – Rich Seller Sep 11 '09 at 17:41
  • That looks like an ajax query to me ;) My idea is that instead doing another $.get request with pagesize=30 append it to all the links on the page. So that when click on something you won't see a page with 1 result. – Pomyk Sep 11 '09 at 17:49
  • yes as you say adding pagesize to the page links is only a partial answer, it won't work if there is another tab open on the same site. – Rich Seller Sep 11 '09 at 19:39