16

I am implementing a Javascript module manager that loads javascript files via XHR object. The problem of this method is resources caching:

  • Firstly, XHR rely on in-built browser caching mechanism which is OK but it's behaviour depends on the browser implementation.
  • Also there is a localStorage and there is a basket.js which uses localStorage to cache downloaded scripts, the problem is in limited size of storage which is usually 5-10MB. Besides, localStorage is a shared place for many scripts which also use it to store data.
  • And there is a Cache interface of the ServiceWorker API, but it is available only in ServiceWorker runtime so it doubtingly fit my needs.

Do anyone know some smart old or new javascript caching technique he's using in his project, or maybe heard of?

Note: Please, don't propose to use jQuery .ajax which is an interface to XHR, or any other library that implements an interface to in-built Javascript features.

Edit: There have been some valuable proposes:

  • Use library called localForage. The library represents a unified API to IndexedDB, WebSQL and localStorage, which one is used depends on browser.
  • Use IndexedDB which is truly powerfull storage with no significant space limits. The only concern is that only modern browsers implement IndexedDB.
Eugene Tiurin
  • 4,019
  • 4
  • 33
  • 32
  • 2
    Is there any specific requirement about persist this cache after page refresh? Do you need to use local storage? – wilsotobianco Nov 18 '15 at 03:56
  • @wilsotobianco yes, the intention is to store it forever not depending on any circumstances, something similar localStorage with less space limits would be perfect – Eugene Tiurin Nov 18 '15 at 04:03
  • 2
    What are you trying to accomplish specifically? What are you trying to save? – wilsotobianco Nov 18 '15 at 04:13
  • @wilsotobianco the content to save is text - Javascript code – Eugene Tiurin Nov 18 '15 at 04:14
  • 1
    did you check this question -> http://stackoverflow.com/questions/17104265/caching-a-jquery-ajax-response-in-javascript-browser ? – wilsotobianco Nov 18 '15 at 04:30
  • @wilsotobianco the answer to that question propose to cache data in period of page lifetime which means that after page reload all cache is gone. Others propose localStorage (and sessionStorage) which, I mentioned, have size limitation. – Eugene Tiurin Nov 18 '15 at 04:41
  • Understood. The only way I can think of is using local storage, I guess form 5 to 10Mb should be enough to cache a lot of text right? – wilsotobianco Nov 18 '15 at 04:43
  • @wilsotobianco right, in fact basket.js uses localStorage for caching, though it's developer admits it's drawback. Also basket flush localStorage in case when it's full, which means that someone's else script data is simply removed. – Eugene Tiurin Nov 18 '15 at 04:53
  • So a custom implementation of local storage handling would work for you? – wilsotobianco Nov 18 '15 at 04:56
  • @wilsotobianco Unfortunately no. For my idea to work, I need a more confident solution to use, like ServiceWorker's Cache API that would run in primary runtime. – Eugene Tiurin Nov 18 '15 at 05:04
  • 1
    You want a solution which is written in Vanilla Javascript and which doesn't rely on local- or session storage, right? –  Nov 18 '15 at 15:20
  • 1
    `XHR` doesn't have a built-in caching mechanism. And what do you mean by _"depends on the browser implementation"_? A browser usually caches every `GET` response it can. You can take a look into IndexedDB. – a better oliver Nov 18 '15 at 22:39
  • 1
    @zeroflagL IndexedDB seem to be best solution for me, the only concern is that only modern browsers implement it – Eugene Tiurin Nov 19 '15 at 02:36
  • Did you think about use a CDN? – Luiz Mitidiero Nov 21 '15 at 19:34
  • so which version of which broswers do you need to support since you are concerned about - IndexedDB – Mauricio Gracia Gutierrez Nov 24 '15 at 06:05
  • For more details for this answer visit my blog - https://maograciag.wordpress.com/2015/11/24/what-are-possible-techniques-to-cache-an-ajax-response-in-javascript/ – Mauricio Gracia Gutierrez Nov 24 '15 at 06:23

5 Answers5

7

This is specific for JQUERY....

Your can make ajax set up as cached.

    $.ajaxSetup({ cache: true});

and if for specific calls you don't want to make cached response then call

 $.ajax({
        url: ...,
        type: "GET",
        cache: false,           
        ...
    });

If you want opposite (cache for specific calls) you can set false at the beginning and true for specific calls

If you want to store the result of ajax response, you can make use of Local Storage. All the modern browsers provides you storage apis. You can use them (localStorage or sessionStorage) to save your data.

All you have to do is after receiving the response store it to browser storage. Then next time you find the same call, search if the response is saved already. If yes, return the response from there; if not make a fresh call.

Smartjax plugin also does similar things; but as your requirement is just saving the call response, you can write your code inside your jQuery ajax success function to save the response. And before making call just check if the response is already saved.

Mauricio Gracia Gutierrez
  • 10,288
  • 6
  • 68
  • 99
Somnath Muluk
  • 55,015
  • 38
  • 216
  • 226
3

Since indexeddb is a method used for storing data client-side, allows indexed database queries.

And this are the supported browsers http://caniuse.com/#feat=indexeddb

And this are the only issues

Firefox (prior to version 37) and Safari do not support IndexedDB inside web workers.
Not supported in Chrome for iOS or other iOS WebViews.

Chrome 36 and below did not support Blob objects as indexedDB values.

Here is another similar polyfill you can try, but in my (albeit limited) experience, both polyfills are buggy/incomplete. They both also have many open issues on GitHub of people reporting problems. And when I tested one of them (I forget which one) it was significantly slower than native IndexedDB.

Maybe it's possible to create a decent polyfill, but the current ones don't seem to be doing the job.

Should I use WebSQL which was deprecated?

The problem with WebSQL is that it's never going to be supported in IE or Firefox. You could probably get away with WebSQL if you're only targeting mobile browsers, at least until Firefox OS or Windows Phone grabs significant market share.

Are there any plans to support IndexedDB in the future for all the non-supported browsers?

Let's be clear. You're asking about Apple, since everyone else supports IndexedDB in their latest browser (iOS Chrome uses Apple's rendering engine because Apple won't let them do anything else).

Not only does Apple not support IndexedDB, they haven't publicly said anything about it (as far as I can tell... and I have done a fair amount of searching). Which seems pretty weird. So as best I can tell, nobody has any idea if Apple ever plans to support IndexedDB. The conspiracy theorist in me thinks maybe they're trying to sabotage HTML5 apps to force people to write native apps, but that's purely speculation.

In total, this leaves us developers in a pretty shitty situation. There is no good cross-platform solution. I recommend you complain to Apple about it. That's what I've done, and I've asked my users who want to use my IndexedDB-based app on iOS to do the same. Still no word from Apple.

UPDATE - Indexeddb is now supported in iOS 8 as stated in WWDC 2014 - but unfortunately it's broken pretty badly.

Considerin also that Subresource Integrity -

Subresource Integrity enables browsers to verify that file is delivered without unexpected manipulation.

Does not have knowissues? so far ?

The i will suggest that you can go with

Subresource based solution if mobile is your main target

indexeddb if mobile is not your main target and use of the publicly available implementations for mobile

If all of the above sound too complex for you then

var localCache = {
    data: {},
    remove: function (url) {
        delete localCache.data[url];
    },
    //a cached version exists
    exist: function (url) {
        return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
    },
    get: function (url) {
        console.log('Getting in cache for url' + url); //log only!
        return localCache.data[url].data;
    },
    set: function (url, cachedData, callback) {
        localCache.remove(url);
        localCache.data[url] = {
            _: new Date().getTime(),
            data: cachedData
        };
        if ($.isFunction(callback)) callback(cachedData);
    },
    timeout: 600, //in seconds
};

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
    if (options.cache) {
        var complete = originalOptions.complete || $.noop,
            url = originalOptions.url;
        //remove jQuery cache as you have your own localCache
        options.cache = false;
        options.beforeSend = function () {
            if (localCache.exist(url)) {
                complete(localCache.get(url));
                return false;
            }
            return true;
        };
        options.complete = function (data, textStatus) {
            localCache.set(url, data, complete);
        };
    }
});

$(function () {
    var url = 'your url goes here';
    $('#ajaxButton').click(function (e) {
        $.ajax({
            url: url,
            data: {
                test: 'value'
            },
                cache: true,
                complete: doSomething
            });
        });
    });
    //ToDo after ajax call finishes, or cached version retrived
    function doSomething(data) {
        console.log(data);
    }
Community
  • 1
  • 1
Mauricio Gracia Gutierrez
  • 10,288
  • 6
  • 68
  • 99
2

Another specific JQUERY answer ?

Not sure it answers your question but that could help. It s caching ajax calls with a timeout.

In the prefiltering, list the various PHP ajax calls you want to add for caching. In this example, cache is enabled with a 10 minutes timeout.

/*----------------------------*/
/* set ajax caching variables */
/*----------------------------*/
$.set_Ajax_Cache_filters = function () {
    var localCache = {
        timeout: 600000, // 10 minutes
        data: {}, //@type {{_: number, data: {}}}
        remove: function (url) {
            delete localCache.data[url];
        },
        exist: function (url) {
            return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
        },
        get: function (url) {
            return localCache.data[url].data;
        },
        set: function (url, cachedData, callback) {
            localCache.remove(url);
            localCache.data[url] = {
                _: new Date().getTime(),
                data: cachedData
            };
            if ($.isFunction(callback))
                callback(cachedData);
        }
    };

    /*----------------------*/
    /* set ajax pre filters */
    /*----------------------*/
    $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
        // list of allowed url to cache
        if (url !== '..............file.php') {
            return false;
        }
        if (options.cache) {
            var complete = originalOptions.complete || $.noop,
                    url = originalOptions.url;

            options.cache = false;//remove jQuery cache using proprietary one
            options.beforeSend = function () {
                if (localCache.exist(url)) {
                    complete(localCache.get(url));
                    return false;
                }
                return true;
            };
            options.complete = function (data, textStatus) {
                localCache.set(url, data, complete);
            };
        }
    });
};
Mauricio Gracia Gutierrez
  • 10,288
  • 6
  • 68
  • 99
cpugourou
  • 775
  • 7
  • 11
1

To do this, the best technic is using local cache, ajaxPrefilter and ajax cache option, the combination of these three will create you the solid cache you want, that you can easily control Here is a code example:

var localCache = {
    data: {},
    remove: function (url) {
        delete localCache.data[url];
    },
    //a cached version exists
    exist: function (url) {
        return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
    },
    get: function (url) {
        console.log('Getting in cache for url' + url); //log only!
        return localCache.data[url].data;
    },
    set: function (url, cachedData, callback) {
        localCache.remove(url);
        localCache.data[url] = {
            _: new Date().getTime(),
            data: cachedData
        };
        if ($.isFunction(callback)) callback(cachedData);
    },
    timeout: 600, //in seconds
};

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
    if (options.cache) {
        var complete = originalOptions.complete || $.noop,
            url = originalOptions.url;
        //remove jQuery cache as you have your own localCache
        options.cache = false;
        options.beforeSend = function () {
            if (localCache.exist(url)) {
                complete(localCache.get(url));
                return false;
            }
            return true;
        };
        options.complete = function (data, textStatus) {
            localCache.set(url, data, complete);
        };
    }
});

$(function () {
    var url = 'your url goes here';
    $('#ajaxButton').click(function (e) {
        $.ajax({
            url: url,
            data: {
                test: 'value'
            },
                cache: true,
                complete: doSomething
            });
        });
    });
    //ToDo after ajax call finishes, or cached version retrived
    function doSomething(data) {
        console.log(data);
    }

Enjoy your coding

El Don
  • 902
  • 7
  • 14
  • 1
    source: https://stackoverflow.com/questions/17104265/caching-a-jquery-ajax-response-in-javascript-browser/17104536#17104536 – TecHunter Jul 08 '19 at 07:56
0

Try this might work

    var cache = {};
var formatTweets(info) {  
    //formats tweets, does whatever you want with the tweet information
};

//event
$('myForm').addEvent('submit',function() {
    var handle = $('handle').value; //davidwalshblog, for example
    var cacheHandle = handle.toLowerCase();
    if(cache[cacheHandle] != "undefined") {
        formatTweets(cache[cacheHandle]);
    }
    else {
        //gitter
        var myTwitterGitter = new TwitterGitter(handle,{
            count: 10,
            onComplete: function(tweets,user) {
                cache[cacheHandle] = tweets;
                formatTweets(tweets);
            }
        }).retrieve();
    }
});
Faisal
  • 61
  • 1
  • 12