34

By Default $.getScript() disables caching and you can use $.ajaxSetup and set caching to true. When testing if the script is actually cached with Firebug most of the time the script is coming back at 200 (Which means the script is a fresh copy) and one in maybe 20 or 30 times it will come back 304 (meaning it used a cached version). Why is it getting a new copy the vast majority of the time?

$.ajaxSetup({
    cache: true
 });

 $.getScript( scriptFile );

The files that getScript retrieves have not been edited and the requests are a page change apart.

Case
  • 4,244
  • 5
  • 35
  • 53
  • 1
    is there a consistent difference in time between hard refresh responses and regular refresh responses? – Asad Saeeduddin Oct 14 '12 at 16:23
  • 1
    Good observation. I would create a jsfiddle and file a defect at http://bugs.jquery.com/newticket – Denys Kniazhev-Support Ukraine Oct 14 '12 at 16:24
  • 1
    Is the URL changing between requests? Have you tried viewing the cache control headers of the URL that is being downloaded? – Matt Whipple Oct 14 '12 at 16:25
  • 6
    Chrome shows a 200 response code even when it also notes a transaction as being satisfied "from cache". – Pointy Oct 14 '12 at 16:31
  • Firebug is firefox, not chrome. URL does not change between requests. – Case Oct 14 '12 at 16:34
  • I know what Firebug is. My point is that assuming that a 200 response means that the cache was bypassed may not be correct. You could use a packet sniffer to see if there's actually an HTTP request being made. Not also that exactly what happens depends on the cache-control headers sent by the site on the original request. – Pointy Oct 14 '12 at 17:20
  • 1
    Your question cannot receive a decent answer until you post the http response headers that your specific webserver sends for `scriptFile`. – goat Oct 14 '12 at 19:31
  • Pointy please leave an answer saying it is a bug in chrome and you can add from me that its ALSO a bug in firefox that it will tell you it is NOT from cache when it in fact is. – Case Oct 16 '12 at 19:47

6 Answers6

31

First lets clarify what is means that the jQuery disables the caching.

When jQuery disables the cache is means that is force the file to be load it again by the browser with some kind of trick, eg by adding one extra random number as parameter at the end of the url.

When jQuery have enable the cache, is not force anything and let the cache that you have set on the header of this file. Which means that if you do not have set on the header of the files parameters to keep it on browser cache, the browser will try to load it again by some methods.

So with enable the cache by jQuery you must also have been set the correct cache headers on your static files to be keep on browser cache, or else browser may try to load them again.

For files that browser see the created date on header, then is connect to the server asking the header again, is compare it and if is not have change then is not load it again, but is make one call to the server.

For files that you have set the a max age, and not ask the server till that date, then the browser is direct load it from the cache if he finds it.

To summarize:
The cache:true is let the browser decide for the cache of this file from the header you send.
The cache:false is force the file to be load again.

Some relative to cache questions:
caching JavaScript files
IIS7 Cache-Control

Tthe inside code
The getScript() is calling the jQuery.get() witch is a shorthand Ajax function of

$.ajax({
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

So by calling the getScript() you make an ajax call, and the jQuery did not keep any kind of cache of your files if this is what you think at the first place.

Custom function to load the sripts
If you do not won to make a global cache:true, and you need only some files to be loaded with cache:true, you can make a custom function as:

function getScriptCcd(url, callback)
{
    jQuery.ajax({
            type: "GET",
            url: url,
            success: callback,
            dataType: "script",
            cache: true
    });
};

This is not affected by the global cache parameter and is load the script files with out adding anything non-cache parameters at the end.

João Pimentel Ferreira
  • 14,289
  • 10
  • 80
  • 109
Aristos
  • 66,005
  • 16
  • 114
  • 150
  • I've done all of that, and the caching still works at best 5% of the time. Perhaps I did not explain it well enough. I have made a custom function like the example that is on Jquerys own page that suggested a function called [cachedScript](http://api.jquery.com/jQuery.getScript/), which in turn does the same thing. However, after testing the results are not promising. Test your script and see if your results are better then mine. – Case Oct 14 '12 at 19:38
  • @Iscariot Maybe I did not understand it well. If you use the `getScriptCcd` as I have type it, you get better cache ? can you also see if your files contains extra random number parameters at the end ? if yes then they are not cached. – Aristos Oct 14 '12 at 19:42
  • I explained in the question that I used $.ajaxSetup({cache: true }); Which disables the random numbers at the end. Look at the script I linked you to in the comment. Then test it for yourself. – Case Oct 14 '12 at 19:44
  • @Iscariot Ok, then you also need to add on your script files, the correct cache headers to be keeped on browser. – Aristos Oct 14 '12 at 19:45
  • @Iscariot yes, I mean to set on the webserver some headers for the cache, what language do you use and what web server ? (I know well the asp.net and the iis) – Aristos Oct 14 '12 at 20:27
  • Okay, then yes it still fails. Please test the script yourself. – Case Oct 14 '12 at 22:06
  • @Iscariot I have test it, to me its work, you need to add the correct headers to stay on browser cache. The browser is very clear, is says: Status Code: 200 OK from cache, 0ms to get it, 0kb load. – Aristos Oct 15 '12 at 05:47
  • 200 is not from cache. 304 is from cache. – Case Oct 15 '12 at 10:42
  • @Iscariot the 200 can be read direct from cache, the 304 can ask the server and then decide. 200 and say that is readed from cache. No data is loading, is very clear. Do not make me send you screen shot. – Aristos Oct 15 '12 at 11:18
  • [Code Information](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) If you are using Chrome and NOT firefox it can be wrong, please see other comments on the first post. I am not trying to argue I really want to get this to work, and it's not working for me. I have even tried other webservers to be 100% sure. Can you show me where you tested it? I will happily show you my tests if you'd like to see them. – Case Oct 15 '12 at 17:50
  • @Iscariot I check it now and with firfox and see the same results. At least to me it say, 200ok, but in the analytic is not get anything from the net, is read them from the cache. Anyway if I find time I make a page with some test and see whats happens. – Aristos Oct 15 '12 at 18:31
  • I am glad your getting the same results I was. Thank you very much for testing it. If you come up with a solution please let me know. – Case Oct 15 '12 at 22:52
  • I m loading a html template via ajax,but strangely the cached copy is showing more time compared to the `?timestamp` appended one in the browser console – techie_28 Jul 05 '18 at 05:44
  • @techie_28 very difficult to answer - this small details needs a lot of test to find out whats happens. – Aristos Jul 05 '18 at 13:16
11

There is an error as of the date this question was posted where both Firefox and Chrome would state that a script is not being loaded from Cache when it indeed is. As of the date of this answer this issue still exists. The easiest way to test is to use console.log and send out a version number.

To cache a dynamically loaded script it it simply done by using the following code.

function onDemandScript ( url, callback ) {
    callback = (typeof callback != 'undefined') ? callback : {};

    $.ajax({
         type: "GET",
         url: url,
         success: callback,
         dataType: "script",
         cache: true
     });    
}

For development you should comment out cache: true.

Case
  • 4,244
  • 5
  • 35
  • 53
  • How reliable is the ifModified flag for local testing? Could that be used alongside 'cache: true' in a dev environment? – dave Jul 25 '14 at 06:43
  • Awesome Iscariot it retains the same syntax as $.getScript(), and it caches stuff waoooooooo – Sundara Prabu May 10 '17 at 14:48
4

By default, $.getScript() sets the cache setting to false. This appends a timestamped query parameter to the request URL to ensure that the browser downloads the script each time it is requested.

jQuery doc site has a nice extension for not appending a timestamp to the request and bypass the cache:

jQuery.cachedScript = function( url, options ) {

  // Allow user to set any option except for dataType, cache, and url
  options = $.extend( options || {}, {
    dataType: "script",
    cache: true,
    url: url
  });


  // Use $.ajax() since it is more flexible than $.getScript
  // Return the jqXHR object so we can chain callbacks
  return jQuery.ajax( options );
};

// Usage
$.cachedScript( "ajax/test.js" ).done(function( script, textStatus ) {
  console.log( textStatus );
});

Source

gdoron
  • 147,333
  • 58
  • 291
  • 367
4

There's a better option actually, you can turn ON caching for certain requests, for example:

$.ajaxPrefilter(function( options ) {
  if ( options.type==='GET' && options.dataType ==='script' ) {
      options.cache=true;
  }
});
Rico Chen
  • 2,260
  • 16
  • 18
2

I know this is an old post, and the existing answer is the real answer, but touching on Iscariot's concern IT REALLY IS CACHING (at least kinda sorta). This is just a quirk of firefox. Maybe this will prove useful to others who are confused by this quirk.

I tested this concept with a REALLY LARGE javascript file that defines google map polygons for the Idaho DOT district boundaries based on arrays of tens of thousands of latlons (the uncompressed file size is 2,806,257, but I run it through a compression process). Using the following javascript

// Grab polys if not already loaded
if (typeof(defaults.data.polys) === 'undefined') {
    /*$.getScript('/Scripts/ScriptMaster.php?file=Districts', function () {});*/
    $.ajax({
        type: "GET",
        url: '/Scripts/ScriptMaster.php?file=Districts',
        success: function() {
            defaults.data.polys = getPolys();
            data.polys = defaults.data.polys;
        },
        dataType: "script",
        cache: true
    });
}

and you can see the relevant php (you don't want the actual Districts.js file it would take too much space on this post, so here's ScriptMaster.php)

<?php
require_once('../settings.php');

if (!isset($_GET['file'])) die();
$file = $_GET['file'];
$doCache = $file == 'Districts';

header('Content-type: application/x-javascript');
if ($doCache) {
    // This is a luxury for loading Districts.js into cache to improve speed
    //  It is at the top because firefox still checks the server for
    //  headers even when it's already cached
    $expires = 7 * 60 * 60 * 24; // set cache control to expire in a week (this is not likely to change)
    header('Cache-Control: max-age='.$expires.', must-revalidate');
    header('Last-modified: Fri, 3 May 2013 10:12:37 GMT');
    header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expires).'GMT');
    header('Pragma: public');
}

ob_start("compress");
require_once($file.".js");
ob_end_flush();

function compress($buffer) {
    global $doCache;
    if (DEV_MODE && !$doCache) return $buffer;
    /* remove comments */
        $buffer = preg_replace('/\/\/.+?$/m', '', preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer));
    /* remove tabs, spaces, new lines, etc. */
        $buffer = str_replace(array("\r\n", "\r", "\n", "\t", '  ', '    ', '    '), '', $buffer);
    /* remove unnecessary spaces */
        $buffer = str_replace(': ', ':', $buffer);
        $buffer = str_replace(' :', ':', $buffer);
        $buffer = str_replace(', ', ',', $buffer);
        $buffer = str_replace(' ,', ',', $buffer);
        $buffer = str_replace('; ', ';', $buffer);
        $buffer = str_replace(' ;', ';', $buffer);
        $buffer = str_replace('{ ', '{', $buffer);
        $buffer = str_replace(' {', '{', $buffer);
        $buffer = str_replace('} ', '}', $buffer);
        $buffer = str_replace(' }', '}', $buffer);

    if ($doCache) { header('Content-Length: '.strlen($buffer)); }

    return $buffer;
}
?>

It's important to note that calling php's header functions BEFORE the script even executes the string you're going to print as unlike chrome and possibly (probably, I'm just too lazy to check) other browsers firefox appears to make a ping to server to check for headers before using cache. Maybe with more research you could determine if this pertains to elements in as equally as it does with ajax (probably not).

So I did five test runs showing the load times for this script with ajax as stated in firebug. Here are the results

#results loading the script after clearing cache (yes those are seconds, not ms)
200 OK      4.89s
200 OK      4.9s
200 OK      5.11s
200 OK      5.78s
200 OK      5.14s

#results loading the page with control+r
200 OK      101ms
200 OK      214ms
200 OK      24ms
200 OK      196ms
200 OK      99ms
200 OK      109ms

#results loading the page again by navigating (not refreshing)
200 OK      18ms
200 OK      222ms
200 OK      117ms
200 OK      204ms
200 OK      19ms
200 OK      20ms

As you can see, my localhost server to web client connection is not the most consistent and my laptop specs are a little shabby (single core processor and all, it's a few years old too) BUT THE POINT is there is a significant drop in load time after the cache is loaded.

[Also in case anyone's curious without the compression script (not like tabs, spaces or new lines are wasted, it just has to be readable still) takes somewhere between 7-8 seconds to load, but I'm not going to do that five times]

So never fear, it really is caching. For smaller scripts that only take ms's to load you may not notice the difference in firefox, honestly; simply because it checks for headers from the server. I know this because of the load time change from moving those header functions from the end of the script to the start. If you have those functions after php goes through the string it takes longer to load.

Hope this helps!

Assimilater
  • 944
  • 14
  • 33
  • Read my initial posts comments. The answer that guy answered is in fact entirely wrong. However I posted the answer in the OP. – Case May 11 '13 at 20:04
0

What do you perhaps are looking for is a getScriptOnce function, which basically, if it knows that the file was already loaded successfully, does not load again such file when such function is called.

I wrote such function. You can test with the Network tab in Firebug or Chrome dev tools. This just loads the same file once. You just need to copy to your files the getScriptOnce function and the global array ScriptArray

var getScriptOnce = (function(url, callback) {
  var scriptArray = []; //array of urls
  return function (url, callback) {
      //the array doesn't have such url
      if (scriptArray.indexOf(url) === -1){
          if (typeof callback === 'function') {
              return $.getScript(url, function(script, textStatus, jqXHR) {
                  scriptArray.push(url);
                  callback(script, textStatus, jqXHR);
              });
          } else {
              return $.getScript(url, function(){
                  scriptArray.push(url);
              });
          }
      }
      //the file is already there, it does nothing
      //to support as of jQuery 1.5 methods .done().fail()
      else{
          return {
              done: function () {
                  return {
                      fail: function () {}
                  };
              }
          };
      }
  }
}());

/*#####################################################################*/
/*#####################################################################*/


//TEST - tries to load the same jQuery file twice
var jQueryURL = "https://code.jquery.com/jquery-3.2.1.js";
console.log("Tries to load #1");
getScriptOnce(jQueryURL, function(){
  console.log("Loaded successfully #1")
});

//waits 2 seconds and tries to load again
window.setTimeout(function(){
  console.log("Tries to load #2");
  getScriptOnce(jQueryURL, function(){
    console.log("Loaded successfully #2");
  });
}, 2000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
João Pimentel Ferreira
  • 14,289
  • 10
  • 80
  • 109