Fast ASP.NET web page loading by downloading multiple javascripts in batch

A web page can load a lot faster and feel faster if the
javascripts on the page can be loaded after the visible content has
been loaded and multiple javascripts can be batched into one
download. Browsers download one external script at a time and
sometimes pause rendering while a script is being downloaded and
executed. This makes web pages load and render slow when there are
multiple javascripts on the page. For every javascript reference,
browser stops downloading and processing of any other content on
the page and some browsers (like IE6) pause rendering while it
processes the javascript. This gives a slow loading experience and
the web page kind of gets ‘stuck’ frequently. As a result, a web
page can only load fast when there are small number of external
scripts on the page and the scripts are loaded after the visible
content of the page has loaded.

Here’s an example, when you visit
http://dropthings.omaralzabir.com, you see a lot of Javascripts
downloading. Majority of these are from the ASP.NET AJAX framework
and the ASP.NET AJAX Control Toolkit project.

"/wp-content/images/19BEAE5BF8B59397237391168AB73D55.png">
Andysnap_003 "/wp-content/images/B4E5A099945EC242ED772212D5309E3C.png"
border="0" width="619" height="269">

Figure: Many scripts downloaded on a typical ASP.NET AJAX page
having ASP.NET AJAX Control Toolkit

As you see, browser gets stuck for 15 times as it downloads and
processes external scripts. This makes page loading “feel” slower.
The actual loading time is also pretty bad because these 15 http
requests waste 15*100ms = 1500ms on the network latency inside USA.
Outside USA, the latency is even higher. Asia gets about 270ms and
Australia gets about 380ms latency from any server in USA. So,
users outside USA wastes 4 to 6 seconds on network latency where no
data is being downloaded. This is an unacceptable performance for
any website.

You pay for such high number of script downloads only because
you use two extenders from AJAX Control Toolkit and the
UpdatePanel of
ASP.NET AJAX.

If we can batch the multiple individual script calls into one
call like "text-decoration:underline;">Scripts.ashx as shown in the
picture below and download several scripts together in one shot
using an HTTP Handler, it saves us a lot of http connection which
could be spent doing other valuable work like downloading CSS for
the page to show content properly or downloading images on the page
that is visible to user.

"/wp-content/images/C8DA62DC63C2001E079C471F51A4416F.png">
Andysnap_002 "/wp-content/images/48506237841271894AE56CDCCC944B7E.png"
border="0" width="499" height="69">

Figure: Download several javascripts over one connection and save
call and latency

The Scripts.ashx
handler can not only download multiple scripts in one shot, but
also has a very short URL form. For example:

"text-decoration:underline;">/scripts.ashx?initial=a,b,c,d,e&/

Compared to conventional ASP.NET "text-decoration:underline;">ScriptResource URLs like:

"text-decoration:underline;">/ScriptResource.axd?d=WzuUYZ-Ggi7-B0tkhjPDTmMmgb5FPLmciWEXQLdjNjt
bmek2jgmm3QETspZjKLvHue5em5kVYJGEuf4kofrcKNL9z6AiMhCe3SrJrcBel_c1
&t=633454272919375000

The benefits of downloading multiple Javascript over one http
call are:

  • Saves expensive network roundtrip latency where neither browser
    nor the origin server is doing anything, not even a single byte is
    being transmitted during the latency
  • Create less “pause” moments for the browser. So, browser can
    fluently render the content of the page and thus give user a fast
    loading feel
  • Give browser move time and free http connections to download
    visible artifacts of the page and thus give user a “something’s
    happening” feel
  • When IIS compression is enabled, the total size of individually
    compressed files is greater than multiple files compressed after
    they are combined. This is because each compressed byte stream has
    compression header in order to decompress the content.
  • This reduces the size of the page html as there are only a few
    handful of script tag. So, you can easily saves hundreds of bytes
    from the page html. Especially when ASP.NET AJAX produces gigantic
    WebResource.axd and
    ScriptResource.axd
    URLs that have very large query parameter

The solution is to dynamically parse the response of a page
before it is sent to the browser and find out what script
references are being sent to the browser. I have built an http
module which can parse the generated html of a page and find out
what are the script blocks being sent. It then parses those script
blocks and find the scripts that can be combined. Then it takes out
those individual script tags from the response and adds one script
tag that generates the combined response of multiple script
tags.

For example, the homepage of "http://www.dropthings.com">Dropthings.com produces the
following script tags:

< script type="text/javascript">
...
//]]>

< script src="/Dropthings/WebResource.axd?d=_w65Lg0FVE-htJvl4_zmXw2&t=633403939286875000" 
type="text/javascript"> ... < script src="Widgets/FastFlickrWidget.js" type="text/javascript"> < script src="Widgets/FastRssWidget.js" type="text/javascript"> < script src="/Dropthings/ScriptResource.axd?d=WzuUYZ-Ggi7-B0tkhjPDTmMmgb5FPLmciWEXQLdj
Njtbmek2jgmm3QETspZjKLvHue5em5kVYJGEuf4kofrcKNL9z6AiMhCe3SrJrcBel_c1
&t=633454272919375000"
type="text/javascript"> < script type="text/javascript"> // ... < script src="/Dropthings/ScriptResource.axd?d=WzuUYZ-Ggi7-B0tkhjPDTmMmgb5FPLmciWEXQLdjNjtbmek2j
gmm3QETspZjKLvHIbaYWwsewvr_eclXZRGNKzWlaVj44lDEdg9CT2tyH-Yo9jFoQij_XIWxZNETQkZ90
&t=633454272919375000"
type="text/javascript"> < script type="text/javascript"> ... < script type="text/javascript"> ... < script type="text/javascript" charset="utf-8"> ... < script src="Myframework.js" type="text/javascript"> < script type="text/javascript"> ... < script type="text/javascript">if( typeof Proxy == "undefined" ) Proxy = ProxyAsync; < script type="text/javascript"> ... < script src="/Dropthings/ScriptResource.axd?d=WzuUYZ-Ggi7-B0tkhjPDTmMmgb5FPLmciWEXQLdjN
jtbmek2jgmm3QETspZjKLvH-H5JQeA1OWzBaqnbKRQWwc2hxzZ5M8vtSrMhytbB-Oc1
&t=633454272919375000"
type="text/javascript"> < script src="/Dropthings/ScriptResource.axd?d=BXpG1T2rClCdn7QzWc-HrzQ2ECeqBhG6oiVakhRAk
RY6YSaFJsnzqttheoUJJXE4jMUal_1CAxRvbSZ_4_ikAw2
&t=633454540450468750"
type="text/javascript"> < script src="/Dropthings/ScriptResource.axd?d=BXpG1T2rClCdn7QzWc-HrzQ2ECeqBhG6oiVakhRA
kRYRhsy_ZxsfsH4NaPtFtpdDEJ8oZaV5wKE16ikC-hinpw2
&t=633454540450468750"
type="text/javascript"> < script src="/Dropthings/ScriptResource.axd?d=BXpG1T2rClCdn7QzWc-HrzQ2ECeqBhG6oiVakhRAk
RZbimFWogKpiYN4SVreNyf57osSvFc_f24oloxX4RTFfnfj5QsvJGQanl-pbbMbPf01
&t=633454540450468750"
type="text/javascript">
...

< script type="text/javascript"> ...

As you see, there are lots of large script tags, in total 15 of
them. The solution I will show here will combine the script links
and replace with two script links that download 13 of the
individual scripts. I have left two scripts out that are related to
ASP.NET AJAX Timer extender.


< script type="text/javascript"> ...


< script type="text/javascript" src="Scripts.ashx?initial=a,b,c,d,e,f&/dropthings/">
< script type="text/javascript"> ... < script type="text/javascript"> ... < script type="text/javascript"> ... < script type="text/javascript"> ... < script type="text/javascript">if( typeof Proxy == "undefined" ) Proxy = ProxyAsync; < script type="text/javascript"> ... < script src="/Dropthings/ScriptResource.axd?d=WzuUYZ-..." type="text/javascript"> < script src="/Dropthings/ScriptResource.axd?d=BXpG1T2..." type="text/javascript">
< script type="text/javascript" src="Scripts.ashx?post=C,D,E,F,G,H,I,J&/dropthings/"> < script type="text/javascript"> ...

As you see, 13 of the script links have been combined into two
script links. The URL is also smaller than majority of the script
references.

There are two steps involved here:

  1. Find out all the "text-decoration:underline;">script tags being emitted
    inside generated response HTML and collect them in a buffer. Move
    them after the visible artifacts in the HTML, especially the

    tag
    that contains the generated output of all ASP.NET controls on the
    page
  2. Parse the buffer and see which script references can be
    combined into one set. The sets are defined in a configuration
    file. Replace the individual script references with the combined
    set reference.

The whole solution is explained in this CodeProject article:

Fast ASP.NET web page loading by downloading multiple
javascripts after visible content and in batch
"http://www.codeproject.com/KB/aspnet/fastload.aspx" href=
"http://www.codeproject.com/KB/aspnet/fastload.aspx">http://www.codeproject.com/KB/aspnet/fastload.aspx

You should be able to use this approach in any ASP.NET (even
better if AJAX) application and give your site a big performance
boost.

If you like the idea, please vote for me.

"http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fwww.codeproject.com%2fKB%2faspnet%2ffastload.aspx">
"http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.codeproject.com%2fKB%2faspnet%2ffastload.aspx"
alt="kick it on DotNetKicks.com" border="0">

21 Comments

  1. omar: Yeah you are right, but now there is also this coming

    “ASP.NET AJAX Script Combining Support

    .NET 3.5 SP1 introduces a new element on the server control, which allows you to declaratively define multiple script references within it. All the script references within the CompositeScript element are combined together on the server and served as a single script to the client, reducing the number of requests to the server and improving page load time for ASP.NET AJAX applications.

    The script combining feature supports both path based scripts and assembly resource based scripts, and dynamically serves up the combined scripts using the ScriptResources.axd handler.

    ” from weblogs.asp.net/…/visual-studio-2008-and-net-framework-3-5-service-pack-1-beta.aspx

  2. Very cool! I guess my solution is for .NET 2.0 and 3.0 guys then.

  3. @ omar

    I think your solution is useful to .NET 3.5 applications too.

    Not everyone wants to use the ASP.NET AJAX framework, you know :)

  4. Apologies for the sparseness of my posting the last few weeks – work and life have been busy here lately

  5. Apologies for the sparseness of my posting the last few weeks – work and life have been busy here lately

  6. Does compositive control also include the webresources generated by asp.net? It seems it doesnt.

  7. I use fiddler to debug. What browser add in you used to capture your script output?

  8. Jose,

    The new .NET3.5 SP1 control does not combine webresource.axd calls which my solution does.

    omar,

    I used Firebug plugin of Firefox.

  9. Article Updated: Now you can prevent a script tag from being moved by setting a pin=”yes” attribute on the script tag. See the article for details.

    This is useful for Google Ads or Analytics scripts which need to be in a fixed position on the page.

  10. In CombineScripts had to modify your regex to the following, to make sure it worked in all combinations of scripts generated on my page.:

    private static Regex _FindScriptTags = new Regex(@”“, RegexOptions.Compiled);

  11. you should pin the script that has fun() so that it does not get moved.

  12. If my div have onmousemove='fun();' that will cause error. It's possible to solve it?

  13. omar, sorry, because i use window.location.reload(); If before action is postback, then reload will be postback, sorry, it's my problem. thanks.

  14. What's the different with FileSets.xml and FileSets_Production.xml? When will load FileSets_Production.xml? When will load FileSets.xml ?

  15. Thanks, but is there any way to reduce the size of javascript files that AJAX control toolkit uses?

  16. This code is working in IE6 Only.I am getting the following error in IE7 & Mozilla.”Ajax Control Took kit is undefined”.Can anyone help me in solving this issue.

  17. wery interesting site, many useful informations

  18. Iam getting given below error in Global.asax file.

    The type or namespace name 'ScriptDeferFilter' could not be found (are you missing a using directive or an assembly reference?)

    I want to use these function in VB not in C# do you know how to convert C# to VB.

    Please help me.

  19. Pingback: Page Loading Effect, 5 Ways to Optimize Page Loading Time On Dial-up | Page Loading Guidelines & Toolbox

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>