Asynchronous jQuery request timing issue - javascript

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?

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.

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.

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

Related

Manually load update datatable data without calling ajax

I'm trying to avoid making two ajax calls by using .ajax.params() to get the last set of ajax parameters and returning the table data from my first call.
I then pass in my own json to datatables following this pattern
datatable.clear();
datatable.rows.add(newDataArray);
datatable.draw();
from this question.
However my table has ajax set so when draw() is called another ajax call is fired which defeats the point of passing in the data myself. What I need is a way to suppress the ajax call while redrawing the table.
An alternative would be to write my own ajax handling and manually add the data into datatables as above, however I think I would also have to create the ajax parameters myself which would be a pain.
I have created the following datatables plugin that makes it possible to do an ajax load with custom ajax settings for just one time.
var __reload = function ( settings, holdPosition, callback ) {
// Use the draw event to trigger a callback
if ( callback ) {
var api = new _Api( settings );
api.one( 'draw', function () {
callback( api.ajax.json() );
} );
}
if ( settings.oApi._fnDataSource( settings ) == 'ssp' ) {
settings.oApi._fnReDraw( settings, holdPosition );
}
else {
settings.oApi._fnProcessingDisplay( settings, true );
// Cancel an existing request
var xhr = settings.jqXHR;
if ( xhr && xhr.readyState !== 4 ) {
xhr.abort();
}
// Trigger xhr
settings.oApi._fnBuildAjax( settings, [], function( json ) {
settings.oApi._fnClearTable( settings );
var data = settings.oApi._fnAjaxDataSrc( settings, json );
for ( var i=0, ien=data.length ; i<ien ; i++ ) {
settings.oApi._fnAddData( settings, data[i] );
}
settings.oApi._fnReDraw( settings, holdPosition );
settings.oApi._fnProcessingDisplay( settings, false );
} );
}
};
jQuery.fn.dataTable.Api.register( 'ajax.loadOnce()', function ( ajax, callback, resetPaging ) {
return this.iterator( 'table', function ( ctx ) {
store = ctx.ajax;
ctx.ajax = ajax;
__reload( ctx, resetPaging===false, callback );
ctx.ajax = store;
} );
} );
This makes it possible to combine the datatables parameters with custom data and a new url as such
ajax = {
url: url,
data: function (d){
d.value = value;
}
};
table.ajax.loadOnce(ajax);
I had a similar issue. The easiest work around I found was temporarily disabling the AJAX and server side processing, make your changes, and then reset the values like shown below:
function set_datatable_ajax_processing(table, bool) {
table.settings()[0].oFeatures.bServerSide = bool;
table.settings()[0].ajax = bool;
}
set_datatable_ajax_processing(table, false)
// table edits and/or draw done here ...
set_datatable_ajax_processing(table, true)

How to handle two ajax request?

Hello i have sequence like this
$(document).ready(function() {
$("#ctl00_txtsearch").autocomplete({
source: function(request, response) {
$.ajax({
// Here is the code of autocomplete which is requesting
// data and binding as autocomplete
});
});
});
var aa=bindonload();
});
Here is the another function which i want to call on page load
function bindonload() {
$.get( "minicart.aspx#mydatacontent", function( data ) {
var resourceContent = data;
var mini=$(resourceContent).find('div#pnlminicart');
$('#smallcart').html(mini);
});
return false;
}
So , my actual problem is when page is loaded
first of all
bindonload()
called and then autocomplete if textbox have some values right?
But when page is loaded and suddenly i started to write into autocomplete textbox then untill bindlonload function gets executed autocomplete will not work.
I don't have idea how to handle it i have used async:true but its not working i don't want to wait for second process
Thanks in advance....
Well..what i guess..should be..you loaddata() should not be taking too much of time to load.
If there is any way to optimize , look of that.
If you ajax request has a dependency on the other, then you can not make it parallel
If you really intend to make parallel ajax requests, you have to make use of the following:
$.when($.ajax("URL1"), $.ajax("URL2"))
.then(myFunc, myFailure);
Hope it helps..
Note : The Ajax calls should not be dependent
Updated:
$.when( $.ajax( "/page1.php" ), $.ajax( "/page2.php" ) ).done(function( a1, a2 )
{
// a1 and a2 are arguments resolved for the page1 and page2 ajax requests, respectively.
// Each argument is an array with the following structure:
[ data, statusText, jqXHR ]
var data = a1[ 0 ] + a2[ 0 ]; // a1[ 0 ] = "Whip", a2[ 0 ] = " It" if ( /Whip It/.test( data ) ) { alert( "We got what we came for!" );
}});
In the above example , you can see two ajax requests executes parallel
After the two requests are done, i.e on sucess of two functions , the add operation is being performed
Similarly , you can replace $.ajax("/page1.php") with your loaddata() and then
$.ajax("page2.php") with your Auto Complete request.
Both of them will execute Parallely

Javascript & Ajax - better way to populate objects?

I'm an old 'C' programmer and with Javascript always struggle populating my data following ajax calls; i.e. I always resort to using global references. I'd rather be able to pass in the objects I need to update. Here's one example of what I do now - 'app' is the global (I'd have used a pointer in C :))
treeMapp.login = function ( dialog_div, form_div, server_call ) {
// validate the fields
if ( 0 ) {
}
else {
// send to server & parse JSON response (single line)
var jqxhr =
$.getJSON( server_call,
$( "#" + form_div ).serialize() )
.done( function( data, status ) {
if( status == 'success' ) {
// hack!?
app.user.username = data.username;
app.user.organisation = data.organisation;
app.user.loggedIn = true;
//close the dialog
$( '#' + dialog_div ).dialog('close');
}
else {
// login failed
alert( "login failed!" );
}
})
.fail( function() {
alert( "login: server error" );
}); // end var jqxhr =
} // end else (field validation ok)
}; // end treeMapp.login()
What's the best way of updating passed in parameters?
thanks
mini
You can pass app as an argument to your treeMapp.login function, then within the scope of it it would be local.
treeMapp.login = function ( dialog_div, form_div, server_call, app )
You could assign the data object returned by your jQuery result to app.user, thus avoiding the need for element by element assignment.
i.e. app.user = data
However, normally you ensure the global object can self initialise through a method, or you pass a reference to the global object to the method so it can initialise. Directly using assignment to global variables is (with a few exceptions) poor programming in Javascript as in any other language
UPDATE: the following shows an amalgamation of the answers...
treeMapp.login = function ( dialog_div, form_div, server_call, theapp ) {
var app = theapp; // may be needed for scope issue..
// validate the fields
if ( 0 ) {
}
else {
// send to server & parse JSON response (single line)
var jqxhr =
$.getJSON( server_call,
$( "#" + form_div ).serialize() )
.done( function( data, status ) {
if( status == 'success' ) {
// not a hack
$.extend(this.app.user, data, { loggedIn: true })
//close the dialog
$( '#' + dialog_div ).dialog('close');
}
else {
// login failed
alert( "login failed!" );
}
})
.fail( function() {
alert( "login: server error" );
}); // end var jqxhr =
} // end else (field validation ok)
}; // end treeMapp.login()

jQuery load method charset

I'm using the .load() method in jQuery but I've realized that the request to my server should use ISO-8859-1 charset and not UTF-8. The problem is that I can't find how to set load method to use a different encoding. I read that .ajax method has 'content-type' setting to do this, but what about load method? I find load very useful when I need to update data from some divs without refreshing the page.
Any help would be appreciated, thanks.
Using ajaxSetup allows you to specify the settings for new ajax calls.
All subsequent Ajax calls using any function will use the new
settings, unless overridden by the individual calls, until the next
invocation of $.ajaxSetup().
with beforeSend you can provide a callback function to modify the XMLHttpRequest object before it's going to be send. jQuery Reference
Mozilla provides documentation about overrideMimeType():
Overrides the MIME type returned by the server. This may be used, for
example, to force a stream to be treated and parsed as text/xml, even
if the server does not report it as such.This method must be called
before send().
Borrowing code from this answer you could do:
$.ajaxSetup({
'beforeSend' : function(xhr) {
xhr.overrideMimeType('text/html; charset=ISO-8859-1');
},
});
//$('body').append('<div id=qqq>dfsdfsdf</div>')
//$('#qqq').load2('/index.php?showtopic=925 #post-29397','','','text/html; charset=utf-8')
//$('#qqq').load2('/index.php?showtopic=925 #post-29397','','','text/plain; charset=windows-1251')
//
jQuery.fn.load2 = function( url, params, callback, overrideMimeTypeVar) {
if ( typeof url !== "string" && _load ) {
return _load.apply( this, arguments );
}
var selector, type, response,
self = this,
off = url.indexOf(" ");
if ( off >= 0 ) {
selector = jQuery.trim( url.slice( off ) );
url = url.slice( 0, off );
}
// If it's a function
if ( jQuery.isFunction( params ) ) {
// We assume that it's the callback
callback = params;
params = undefined;
// Otherwise, build a param string
} else if ( params && typeof params === "object" ) {
type = "POST";
}
// If we have elements to modify, make the request
if ( self.length > 0 ) {
jQuery.ajax({
url: url,
// if "type" variable is undefined, then "GET" method will be used
type: type,
dataType: "html",
data: params,
// ++++++++++++++++++++++++++++++++++++++++++++++++++
beforeSend: function(x) {
if(x && x.overrideMimeType && overrideMimeTypeVar!=''){
x.overrideMimeType(overrideMimeTypeVar);
}}
// +++++++++++++++++++++++++++++++++++++++++++++++++++
}).done(function( responseText ) {
// Save response for use in complete callback
response = arguments;
self.html( selector ?
// If a selector was specified, locate the right elements in a dummy div
// Exclude scripts to avoid IE 'Permission Denied' errors
jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
// Otherwise use the full result
responseText );
}).complete( callback && function( jqXHR, status ) {
self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
});
}
return this;
};

Caching and invalidating XHR/Ajax response programmatically

Consider a webpage having lot of XHR calls to server and A iframe which again contains lot of XHR calls to server.
Many of this calls are same (Redundant). I do have single communication interface (i.e. set of methods in a javascript object).
How to optimize server calls? Can we cache responses? (I can invalidate stored response when some action happend which may change response), Also This cache should be cleared after page refresh. Is there any such component/technique available?
Regards,
Nachiket
Some form of memoization.
NOTE: you will have to change the following code to accomodate your XHR implementation.
Had to make assumptions since you offered no code.
var cacheXHRWrapper = function ( url , handler ) {
var cache = {};
var queued = {};
var pending = {};
if ( cache[ url ] ) {
handler( cache[ url ] );
} else {
queued[ url ] || ( queued[ url ] = [] ); // I know, call me lazy.
queued[ url ].push( handler );
if ( !pending[ url ] ) {
// might want to adjust this to comply to your XHR implementation
XHR_IMPL.request( url , function ( response ) {
// cache response
cache[ url ] = response;
// serve all queued handlers.
var fn = queued[ url ].shift();
while ( fn ) {
fn( response );
fn = queued[ url ].shift();
}
} );
pending[ url ] = true;
}
}
}
Bonus, queues request handlers (by url) that are already running.

Categories