“Delayed Script Execution” in Opera

September 11, 2008 8:37 pm | 7 Comments

I’ve recently been evangelizing techniques for loading external scripts without blocking the rest of the page. A co-worker at Google pointed out a little known option in Opera: Delayed Script Execution. Opera’s support web site provides this explanation:

Primarily for low bandwidth devices, not well-tested on desktop. Ignore script tags until entire document is parsed and rendered, then execute all scripts in order and re-render.

This option is handy for developers today, and provides a guideline for how script deferral should be built in all browsers.

Loading Scripts Without Blocking and document.write

There are serveral advanced techniques for loading scripts without blocking other downloads and rendering. At recent conferences, I’ve talked about six alternatives for asynchronous script loading. (See slides 14-27 from my OSCON slide deck, for example.)

One limitation of these techniques is that you can’t use document.write, because when a script is loaded asynchronously the browser has already written the document. Hardcore JavaScript programmers avoid document.write, but it’s still used in the real world most notably, and infamously, by ads. A feature of Opera’s “Delayed Script Execution” option is that, even though scripts are deferred, document.write still works correctly. Opera remembers the script’s location in the page and inserts the document.write output appropriately.

An Example

I created a test page that demonstrates how this feature would benefit all browsers. The test page contains a script followed by eight images. The script is programmed to take 4 seconds to download and 4 seconds to execute. The images are each programmed to take 1 second to download. In most browsers, the default behavior is that the browser starts downloading and executing the script first, since it occurs first in the page. The images have to wait 8 seconds for the script to finish before they are downloaded and rendered. The text in the page is also blocked from rendering for 8 seconds, even though it’s already been downloaded before the script. This is because browsers block rendering of anything (text and resources) below a script until the script is done downloading. Let’s look at how this page behaves in popular browsers.

IE 6,7,8
The 8 second script blocks everything else in the page until it’s done downloading and executing. In IE 6,7 the overall page load time is ~12 seconds in IE 6,7 (8 seconds for the script plus 4 seconds for the images to load over two connections). In IE8 it’s ~10 seconds (8 seconds for the script plus 2 seconds for the images to load over six connections).IE has its own asynchronous script loading technique – the DEFER attribute. Clicking the “Load with Defer” button adds this attribute to the SCRIPT tag in the page. The results are disappointing – because the script contains document.write, the entire page is overwritten. The HTML 4.01 spec notes that DEFER should only be used when “the script is not going to generate any document content (e.g., no ‘document.write’ in javascript)”. And this example proves that IE’s built-in mechanism for script deferral should not be used for scripts that contain document.write.

Firefox 2,3
Loading the page in Firefox 2,3 produces results similar to IE — the script blocks the images. In Firefox 2 the overall page load time is ~12 seconds (8 seconds for the script plus 4 seconds for the images to load over two connections). In Firefox 3 it’s ~10 seconds (8 seconds for the script plus 2 seconds for the images to load over six connections).Since Firefox doesn’t support the DEFER attribute, you can click on the “Load Dynamically” button to load the script using one of the advanced non-blocking techniques (“script DOM element”). The page starts off loading great — the entire text is rendered immediately and the images start downloading and rendering quickly, all because the script is being loaded asynchronously. But when the script finally finishes and does its document.write, the entire page is overwritten, similar to what happens in IE. Scripts containing document.write can’t be loaded asynchronously in Firefox.

Safari 526
The script and eight images are downloaded in parallel. (Parallel script loading is a new feature in Safari 526.) But rendering is blocked for the entire 8 seconds while the script is downloaded and executed. Overall page load time is ~8 seconds, since the images and script are downloaded in parallel.To try loading the script asynchronously, click on the “Load Dynamically” button. The text is rendered immediately and the images start downloading and rendering quickly, all because the script is being loaded asynchronously. But when the script finishes and does its document.write, it’s not shown in the page. Scripts containing document.write won’t have their output shown in the page if loaded asynchronously in Safari.

Opera 9.5
Without enabling the “Delayed Script Execution” option, the page load experience is similar to the other browsers: The script is downloaded first, blocking the image downloads and text rendering for 8 seconds. The overall page load is a little faster, ~9 seconds, because Opera supports eight simultaneous connections, so the eight images download in 1 second.Enabling “Delayed Script Execution” provides an exciting alternate experience. The entire page’s text is rendered immediately. The eight images start downloading and rendering right away. In the background the 8-second script is downloading, so the overall page load time is ~8 seconds. The icing on the cake is that when the script is done executing its document.write appears where it should – a purple DIV in the box at the top. It doesn’t overwrite the page and it doesn’t disappear. It goes right where it was supposed to go.

What’s all the excitement?!

One immediate benefit of this Opera preference is that web developers can see the impact of delay-loading their JavaScript. A practice I’m advocating a lot lately is splitting a large JavaScript payload into two pieces, one of which can be loaded using an asynchronous script loading technique. This is often a complex task as the JavaScript payload grows in size and complexity. With this “Delayed Script Execution” feature in Opera, developers can get an idea of how their page would feel before undertaking the heavy lifting.

I’m even more excited about how this shows us what is possible for the future. To be able to have asynchronous script loading and preserve document.write output is like having your cake and eating it too. It’s difficult for users to find this feature in Opera. And it’s beyond the reach of web developers. But if Opera’s “Delayed Script Execution” behavior was the basis for implementing SCRIPT DEFER in all browsers, it would open the door for significant performance improvements by simply adding six characters (“DEFER ”).

This is most significant for the serving of ads. Often ads are served by including a script that contains document.write to load other resources: images, flash, or even another script. Ads are typically placed high in the page, which means today’s pages suffer from slow loading ads because all their content gets blocked. And really, it’s not the pages that suffer, it’s the users. Our experience suffers. Everyone’s experience suffers. If browsers supported an implementation of SCRIPT DEFER that behaved similar to Opera’s “Delayed Script Execution” feature, we’d all be better off.

Food for thought for Safari, Firefox, and IE.

7 Responses to “Delayed Script Execution” in Opera

  1. FYI, it appears you have comments at the top of your RSS feed and as a result it is not valid.

  2. BTW, thanks for pointing out this config option in Opera. I have never heard about it or noticed it before.

  3. Well, interesting that you came across this nearly secret feature. Be warned that enabling it by default may cause compatibility problems with websites – some that do odd things like relying on the exact number of elements before the SCRIPT that is executing, frame breakers that barf due to IFRAMEs and other wonderful weirdness. It’s pretty useful on mobile phones though.

  4. Thanks Steve for an excellence article. Is there a way to load an external script and prevent document.write so it doesn’t overwrite the page. Also I don’t like using iframe.

  5. Hey Steve,

    Thanks for a nice article.

  6. web developpers does not use any document.write, period.

  7. For ads you could do all your external document.write ads at the end of the document and then move those elements to their according positions on document DOM ready. You could do it with ease with a y few lines of jquery.