2

I'd like to implement a robust ajax cache and looking for an appropriate pattern - probably using the new jquery 1.5.2 deferred object.

The top answer here:

How can jQuery deferred be used?

comes close, but where it fails short is if the two ajax requests are fired simultaneously, there will still be 2 requests to the server. Since the response hasn't come in yet, the cache is not yet populated.

I'd like an implementation which would only fire 1 request to the server, but will return the response to both.

Community
  • 1
  • 1
Gal
  • 5,537
  • 1
  • 22
  • 20

1 Answers1

3

From the top of my head, here is something completely untested:

(function( $ ) {
    // Perform a cached ajax request
    // keyFn is a function that takes ajax options
    // and returns a suitable cache key
    jQuery.cachedAjax = function( keyFn ) {
        // Cache for jqXHR objects
        var cache = {};
        // Actual function to perform cached ajax calls
        return function( url, options ) {
            // If url is an object, simulate pre-1.5 signature
            if ( typeof url === "object" ) {
                options = url || {};
                url = undefined;
            // else, add the url into the options  
            } else if ( url ) {
                options = $.extend( {}, options || {} );
                options.url = url + "";
            }
            // Get the cache key
            var key = keyFn( options );
            // If not cached yet, cache it
            if ( !cache[ key ] ) {
                cache[ key ] = $.ajax( options );
            } else {
                // If already cached, ensure success, error
                // and complete callbacks are properly attached
                for( var cbType in { success: 1, error: 1, complete: 1 } ) {
                    cache[ key ][ cbType ]( options[ cbType ] );
                }
            }
            // Return the jqXHR for this key
            return cache[ key ];
        };
    };
})( jQuery ):

// Create a method that caches by url    
jQuery.ajaxCachedByURL = jQuery.cachedAjax(function( options ) {
    return options.url;
};

// Use the method just like ajax      
jQuery.cachedAjax( url, options ).then( successCallback, errorCallback );

The idea is to store the jqXHR in the cache, not the value. As soon as the request is initiated once then it doesn't matter if it has already finished or is running: fact is further calls to the cached ajax method will return the same jqXHR so concurrency is handled transparently.

Julian Aubourg
  • 11,346
  • 1
  • 29
  • 29