Does JavaScript Get Cached? Efficient Browser Experiences
In the current state of the web, performance plays a quintessential role in a user’s experience. Having a fast and fluid web application has a direct effect on the success of a website’s conversions and traffic.
After all, good performance is all about maintaining users, so keeping them engaged will only lead to positive results in any business venture.
When loading a webpage, JavaScript can be one of the heavier assets requested by a website. After being retrieved from the server once, in order to be performant, the user’s browser should ideally not have to request the same JavaScript bundle again.
This is where the process of caching yields benefits. If the user’s browser has access to resources that were already retrieved, they are going to have a quicker experience as a result.
So, if we want a user’s browser not to retrieve any JavaScript asset repeatedly, caching seems like the way to achieve this. But does JavaScript get cached?
In general, most modern browsers will cache JavaScript files. This is standard practice for modern browsers and ensures an optimized loading experience.
Cached assets such as JavaScript will typically be served from the browser’s cache instead of making another request for a resource that has already been retrieved.
This is applicable when a user navigates to a URL that has previously been visited.
Since JavaScript provides the foundation to enable the user’s interaction with a webpage, serving the cached version of the JavaScript results in a reduced time taken to interact.
Google Chrome and most modern browsers provide a run-time environment for their JavaScript called the V8 engine. There are, of course, optimizations that can be made by a developer to get the most out of caching.
Here is some further detail on Google Chrome and most modern browsers.
However, a more passive approach might be more effective here as even though changes can be made to improve caching, the logistics behind browsers caching process is subject to change in its version updates.
This can result in creating caching improvements that are no longer valid after a few months, essentially nullifying the work that went into it.
So, while JavaScript can be optimized for browser caching, the actual type of caching is decided by configurations in an HTTP request.
Let’s explore this further.
What is the function of the cache in HTTP?
HTTP requests play a big role in determining whether a JavaScript asset is cached or not.
The goal of caching in HTTP is to reduce the need to send requests and responses. Reducing both of these will result in fewer network round-trips as well as reduced network bandwidth requirements
With JavaScript being the most common asset requested in order to create user interaction, the reduction of these network round trips bodes well for the user’s experience.
To control how the browser caches JavaScript (and other assets) HTTP requests and responses can be configured with various directives.
These directives control how the browser interacts with the resources it requests.
Let examine the main directives related to caching.
Cache-Control max-age
This setting tells the browser how long it should keep cached data after its initial retrieval.
Typically this is defined in seconds like so:
Cache-Control: max-age=<seconds>
When specifying a custom amount of seconds in this header, we must be wary of potential updates to those files.
For example, this applies if there was an update to a JavaScript file during the time where the browser continued to use the cached version.
This can cause the user to be interacting with an earlier version of the website.
This is a frequently encountered scenario in tech companies, where the developers push a change to the product and the business side is unable to see the updates.
This results in some confusion between both areas of the company but is easily remedied by clearing the cache of the browser.
And while this situation might seem trivial, I have experienced it many times in different jobs, especially during times of urgency and feature crunch. It can cause a small amount of temporary friction between business and development teams.
These custom headers enable quite a lot of flexibility when it comes to configuring browser cache.
No cache
The no-cache directive indicates to the browser that cached versions of the requested resource can not be utilized without checking to see if there has been an updated version of the resource since the initial caching occurred.
Typically, this check is done by using an additional header called an ETag. This is a unique token that helps to differentiate versions of the same resource.
The token will change on the server whenever the resource is updated. If the ETag in the browser is identical to the server then the cached version will be served to the user.
If these tokens don’t match, a fresh version of the resource is downloaded. This process enables the user to get the most up to date version of the assets they are requesting.
No store
The configuration header directive, no-store, acts in a more simplified manner when compared to no-cache. It essentially disallows browsers from storing any versions of resources from returned responses.
In the case of retrieving heavy JavaScript files, this can cause the user’s experience of interaction to be notably slow.
The process of downloading the assets every time is undoubtedly inefficient. However this is not without good reason, the use of no-store can help to disallow the storing of personal data/private information.
Some other Cache-Control request options include:
Cache-Control: max-age=<seconds> Cache-Control: max-stale[=<seconds>] Cache-Control: min-fresh=<seconds> Cache-Control: no-cache Cache-Control: no-store Cache-Control: no-transform Cache-Control: only-if-cached
Some other Cache-Control response options include:
Cache-Control: must-revalidate Cache-Control: no-cache Cache-Control: no-store Cache-Control: no-transform Cache-Control: public Cache-Control: private Cache-Control: proxy-revalidate Cache-Control: max-age=<seconds> Cache-Control: s-maxage=<seconds>
The full list of cache headers and their functionality can be found at the MDN docs.
What is Memoization in JavaScript?
You might wonder if JavaScript has any internal caching capabilities. I mean surely such an acclaimed programming language has some sort of caching built into it?
Well, Memoization in JavaScript is a specific form of caching that allows the storing of values returned from functions based on its parameters.
More precisely, for every input, the first time a function is called, JavaScript will allow it to compute the result as normal. This result can then be stored in memory and the next time the same function is called, the already computed data is returned.
Essentially, Memoization is caching for functions.
This technique is only effective when the function is expected to return the same result. This is also called a pure function.
The Memoization process might look something like this:
- Create references to a function as an input
- Create a cache to hold the result of previous function calls
- Future invocations of that function will return the cached results
- If no cached value exists, call the function and store the returned result in cache
These steps may simplify the process a little bit, so here is one the most clear and concise demonstration of memoization of JavaScript that I’ve found:
Memory Considerations
If there is memory available to be used for memoization, then it can be worth implementing in order to eliminate some repeated code execution. It may not worth the extra effort in programs where memory is already sparse.
Memoization is certainly one of the more interesting aspects of caching in JavaScript. You can spend a significant amount of time trying to optimize your code for caching, however, techniques such as memoization encourage you to reevaluate your code in order to determine if the time put in as well as the extra memory used by the process is worth the effort.
While it is unlikely that you will use memoization wherever possible in your codebase, there are plenty of 3rd party libraries that can do a much better job for us.
Take, for example, Reselect, which provides redux selector utilities that are not recomputed unless one of its arguments changes.
This is very handy in a Redux based application, as these selectors are constantly used to call upon subsections of data in the applications state.
Having the results of the selectors already computed provides efficiency for more performant web applications.
If you don’t want to use 3rd party libraries and you are optimizing your functions for memoization, just ensure that the functions to be memoized are pure functions.
How long does a website stay cached?
So, we’ve already covered the various benefits of caching JavaScript files. This is clearly an important consideration for the user’s experience. In general, browsers provide the user with a very useful utility of persisting data in its cache.
But, surely it can’t store endless amounts of cached data, right? How long does a website actually stay cached?
Well, this very much depends on the browser, settings, and configuration of the HTTP requests and responses.
Typically, browsers reserve a finite amount of disk space to complete a task. If a user stops using the browser completely, the cache can persist indefinitely.
However, if the browser is used occasionally by the user, the stored data will last until the expiration date that was set either by the internal policy of the browser or the configuration of the HTTP headers.
If the user is very active using the browser, the length of time that the cache is stored can be extended to 10 minutes or less.
There is also a minimum size for caches of small JavaScript and other code files. Chrome enforces a minimum size of 1Kib of source code files before it will cache the asset.
The process of caching such a small file is too heavy of an operation relative to the value it provides.
Conclusion
I hope I’ve highlighted the importance of cache in JavaScript and how it provides value to the user experience. Overall, I feel like caching is very pertinent in the current state of the web due to web applications getting larger and larger.
Equally, all JavaScript frameworks used to build web applications are growing in complexity. As a result, JavaScript bundles are increasing in size. even with optimizations from modern asset bundlers like Webpack.
The majority of the logic for the user to interact is contained within these assets, so the ability to have them cached will allow the user to interact much faster on subsequent visits to the same page.