Caching and invalidating XHR/Ajax response programmatically - javascript

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.

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)

Retrieving a specific link from url using javascript

I need to make a function in JavaScript to locate href inside the url that is given, and to return it as a string.
For example: http://stackoverflow.com/
So the function starts with: function example(url) {}
I want to find the first link inside this url that contain the words google.
In this page there is somewhere link like <a href:"http://google.com/asdasdadsa/asdada">
The function is to return the whole link as string.
So basically from what I can gather, you want to look at each link on the page and get the whole URL if it includes some string (i.e. google).
Here's a function that finds the first link matching a certain string:
function checkLinks( searchString ) {
var url;
// Go through each link
$('a').each( function ( ) {
// Check if the search string exists
if( $(this).attr('href').indexOf(searchString) != -1 ) {
url = $(this).attr('href');
// If we've found one, stop the each.
return false;
}
});
return url;
}
I've put together a jsfiddle showing an example of how this function could be used:
http://jsfiddle.net/K9KvS/1/
EDIT:
I've just seen you need to do this on a remote URL. You probably need to use AJAX to load in the code, then run this on the code you have. Unfortunately due to the same origin policy, you can't get this directly, so you'll need to run a server-side script on your server (e.g. using PHP) to load the content of the external page, then an AJAX call from your JS to pull it into your javascript.
Modified version to include an AJAX load of some code, then a find on that code:
// Create a function to do the actual search
function checkLinks( code, searchString ) {
var url;
// Search the code for all <a> tags, the loop over them
$(code).find('a').each( function ( ) {
// Check if there is a match (indexOf returns -1 if not)
if( $(this).attr('href').indexOf(searchString) != -1 ) {
// set the "url" variable to the href
url = $(this).attr('href');
// Stop looping
return false;
}
});
return url;
}
// Now, when the page loads, attach an AJAX call to a button with ID "linkchecker"
$( function ( ) {
$('#linkchecker').click( function( ) {
var code;
// Perform the AJAX call, load the data and call our function above to find "google.com"
$.get( 'load_code.php?url=http://www.google.com', function( data ) {
code = data;
alert( checkLinks( code, 'google.com' ) );
});
});
});
load_code.php would probably look something like this (probably with some error checking, etc):
<?php
$htm = file_get_contents($_GET['url']);
echo $htm;
?>
Update: Using Raw Javascript
We'll modify checkLinks from above to use raw Javascript:
function checkLinks( code, searchString )
{
var url;
// We need to create an HTML document element so we can use javascript dom functions on it.
var doc = document.createElement("html");
doc.innerHTML = code; // put the code into the document
// Get all links in the code
var links = doc.getElementsByTagName("a")
// Loop over all links
for (var i=0; i<links.length; i++) {
// Check if the search string (e.g "google.com") is found in the href of the link
if( links[i].getAttribute("href").indexOf(searchString) != -1 ) {
// Set it to the return value
url = links[i].getAttribute("href");
// stop looping
break;
}
}
return url;
}
So firstly, you need to set up the Ajax request object. The problem is this differs between browsers, so you need an unpleasant bit of code to generate it across them. The following is modified from the tiztag ajax tutorial:
function makeAJAXObject(){
var ajaxRequest; // The variable that makes Ajax possible!
try{
// Opera 8.0+, Firefox, Safari
ajaxRequest = new XMLHttpRequest();
} catch (e){
// Internet Explorer Browsers
try{
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try{
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
// Something went wrong
alert("Your browser broke!");
return false;
}
}
}
return ajaxRequest;
}
Ok, so now we've got our AJAX object, we want to get it to load a page, and tell it how to handle what we get back:
/*
* A function to load a given URL and process the code from it.
* It takes three arguments:
* php_handler The name of the PHP file that will load the code (or ASP, or whatever you choose to use)
* url The URL to be loaded.
* searchString The string to find in the links (e.g. "google.com").
*/
function load_page( php_handler, url, searchString )
{
// Get the ajax object using our function above.
window.ajax = makeAJAXObject( );
// Tell the AJAX object what to do when it's loaded the page
window.ajax.onreadystatechange = function(){
if(window.ajax.readyState == 4){ // 4 means it's loaded ok.
// For simplicity, I'll just alert this, but you would put your code to handle what to do when a match is found here.
alert(checkLinks( window.ajax.responseText, searchString ));
}
}
// Set up the variables you want to sent to your PHP page (namely, the URL of the page to load)
var queryString = "?url=" + url;
// Load the PHP script that opens the page
window.ajax.open("GET", php_handler + queryString, true);
window.ajax.send(null);
}
The final thing is to attach this to a button when the page has loaded:
window.onload = function( ) {
document.getElementById('linkchecker').onclick = function( ) {
load_page('load_page.php', 'http://www.example.com', 'google');
}
}
Please note, there's likely to be built in WinJS functions to handle some of the AJAX stuff, but I've never tried Win 8 app development, so I don't know them!

Posting to Twitter through OAuthSimple.js

I've been stuck on this one for a while. I'm trying to use OAuthSimple.js to interact with Twitter in a Chrome extension I've written.
The signing process seems to work fine for requests to retrieve a user's statuses, but I can't seem to construct a request that will successfully authenticate when I try to retweet, reply, or mark a tweet as favorite.
I'm following the guides here. I have also tried numerous ways of structuring the request, and comparing the request contents against the output of the OAuth tool provided by Twitter ( which seems to check out ), but I'm still getting 401 errors and generic "We couldn't authenticate you" responses.
Here's how I'm trying to form the request:
var sendTwitterRequest = function(url, params, method, callback) {
var request = null;
if ( localStorage.twitterAuthToken ) {
OAuthSimple().reset();
request = OAuthSimple(TwitterConsumerKey,TwitterConsumerSecret).sign({
action:method,
method:"HMAC-SHA1",
dataType:"JSON",
path:url,
parameters:params,
signatures:{
oauth_version:'1.0',
oauth_token:localStorage.twitterAuthToken,
oauth_secret:localStorage.twitterAuthVerifier
}
});
console.log(request);
$j.ajax({
url:request.signed_url,
type:method,
data:request.parameters,
success:callback
});
}
};
Then, making calls into this method like this:
// this works, I get the data and can do stuff with it
sendTwitterRequest('http://api.twitter.com/1/statuses/user_timeline.json?user_id=',null,'GET',someMethod());
// this fails and throws a 401 error every time
sendTwitterRequest("https://api.twitter.com/1/statuses/retweet/"+tweetKey+".json",null,'POST',someOtherMethod());
Am I missing something? Thanks in advance!
It turns out the requests I am creating are fine, I just needed a final one to exchange request tokens for OAuth tokens. I thought this step was covered when the user was prompted for input, but turns out I was wrong.
I also ended up switching from OAuthSimple.js to just OAuth.js, on account of the fact that I could only get OAuth.js to process both the token requests and the timeline requests.
Some of this is pretty specific to what my application is doing, so you will probably need to modify it.
The new sendTwitterRequest method:
var sendTwitterRequest = function(options){
var accessor={
consumerSecret:TwitterConsumerSecret
};
var message={
action:options.url,
method:options.method||"GET",
parameters:[
["oauth_consumer_key",TwitterConsumerKey],
["oauth_signature_method","HMAC-SHA1"],
["oauth_version","1.0"]
]
};
if(options.token){
message.parameters.push(["oauth_token",options.token])
}
if(options.tokenSecret){
accessor.tokenSecret=options.tokenSecret
}
for(var a in options.parameters) {
message.parameters.push(options.parameters[a])
}
OAuth.setTimestampAndNonce(message);
OAuth.SignatureMethod.sign(message,accessor);
try {
$j.ajax({
url:message.action,
async:options.async||true,
type:message.method||'GET',
data:OAuth.getParameterMap(message.parameters),
dataType:options.format||'JSON',
success:function(data) {
if (options.success) {options.success(data);}
}
});
} catch ( e ) {
}
};
And the methods that depend on it:
// asks Twitter for an oauth request token. User authorizes and the request token is provided
requestTwitterToken = function() {
// this is semi-specific to what my extension is doing, your callback string may need
// to be slightly different.
var callbackString = window.top.location + "?t=" + Date.now();
var params = [
[ 'oauth_callback', callbackString ]
];
sendTwitterRequest({
url: "https://api.twitter.com/oauth/request_token",
method: 'POST',
parameters: params,
format: 'TEXT',
success: function(data) {
var returnedParams = getCallbackParams(data);
if ( returnedParams.oauth_token ) {
chrome.tabs.create({
url:"https://api.twitter.com/oauth/authorize?oauth_token=" + returnedParams.oauth_token
});
}
},error:function( e ) {
console.log( 'error' );
console.log( e );
}
});
};
// exchanges the Twitter request token for an actual access token.
signIntoTwitter = function(token, secret, callback) {
var auth_url = "https://api.twitter.com/oauth/access_token";
var authCallback = function(data) {
var tokens = getCallbackParams(data);
localStorage.twitterAuthToken = tokens.oauth_token || null;
localStorage.twitterAuthTokenSecret = tokens.oauth_token_secret || null;
callback();
};
try {
sendTwitterRequest({url:auth_url, method:'POST', async:true, format:'TEXT', token:token, tokenSecret:secret, success:authCallback});
} catch ( e ) {
console.log(e);
}
};
With this, the steps are as follows:
ask Twitter for a token ( requestTwitterToken() ) and provide a callback
in the callback, check to see if a token is provided. If so, it's an initial token
pass the token back to Twitter and open the Twitter auth page, which allows the user to grant access
in the callback to this call, see if an access token was provided
exchange the request token for an access token ( signIntoTwitter() )
After that, I simply use the sendTwitterRequest() method to hit Twitter's API to fetch the timeline and post Tweets.

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;
};

Asynchronous jQuery request timing issue

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.

Categories