Skip to main content

Bug Tracker

Side navigation

#7366 closed enhancement (invalid)

Opened November 01, 2010 08:50AM UTC

Closed December 23, 2010 04:49PM UTC

Last modified March 13, 2012 03:29PM UTC

Asynchronously loading scripts need special care with jQuery.fn.ready

Reported by: taka.atsushi@gmail.com Owned by:
Priority: undecided Milestone:
Component: unfiled Version: 1.4.3
Keywords: needsreview Cc:
Blocked by: Blocking:
Description

Current implementation of jQuery.fn.ready is like this.

    ready: function( fn ) {
        // Attach the listeners
        jQuery.bindReady();
 
        // If the DOM is already ready
        if ( jQuery.isReady ) {
            // Execute the function immediately
            fn.call( document, jQuery );
 
        // Otherwise, remember the function for later
        } else if ( readyList ) {
            // Add the function to the wait list
            readyList.push( fn );
        }

When a script author writes a script that loads via <scirpt> tag, they can do something like this.

$(function() { foo(); });
var foo = function(){ };

Since $() executes the function asynchronously, the "foo" function will be defined. So there is no problem. But if they start to try <script async> or a custom script loader such as LABjs, and if $() becomes synchronous, this script won't work any more because "foo" will be undefined.

In the case of <script async>, you can't be sure if the script comes before or after DOMContentLoaded. It may be working in the local server where script loads in a moment, but not working somewhere where it takes 5 seconds to load the script.

I think jQuery.fn.ready should execute the function asynchronously even if jQuery.isReady is true, so that scripts are more portable.

Attachments (0)
Change History (4)

Changed November 01, 2010 09:19AM UTC by snover comment:1

_comment0: Basically, the complaint here is that because `$.fn.ready` doesn’t unconditionally defer the execution of the function that is passed to it, badly written code with race conditions that incorrectly expect the call will always occur only after the caller has returned will sometimes experience…race conditions. \ \ I’m loathe to call these workarounds since they really are not workarounds but proper coding practice, but the “workarounds” here are: \ \ 1. Use function declarations instead of function literals. \ 2. Use function literals, but define them before calling `$.fn.ready`. \ \ Even if the calls are deferred, you are not guaranteed that a deferred call is going to occur after all asynchronous scripts load, so I am not sure this suggestion will actually solve anything related to the original complaint regarding asynchronously loaded scripts.1288603203180186
keywords: → needsreview
milestone: 1.5

Basically, the complaint here is that because $.fn.ready doesn’t unconditionally defer the execution of the function that is passed to it, badly written code with race conditions that incorrectly expect the call will always occur only after the caller has returned will sometimes experience…race conditions.

I’m loathe to call these workarounds since they really are not workarounds but proper coding practice, but the “workarounds” here are:

1. Use function declarations instead of function literals.

2. Use function literals, but define them before calling $.fn.ready.

Even if the calls are deferred, you are not guaranteed that a deferred call is going to occur after all asynchronous scripts load (only that they will occur after the engine reaches the top of the call stack), so I am not sure this suggestion will actually solve anything related to the original complaint regarding asynchronously loaded scripts.

Changed November 01, 2010 10:30AM UTC by taka.atsushi@gmail.com comment:2

I agree that it's a badly written code, but the I can easily imagine people do it. It's not easy to debug too (in the case of <script async>).

>Use function declarations instead of function literals.

You can't always replace a function declaration with a function literal.

$(function() { foo.bar(); });
var foo = { bar : function() {} };

>Use function literals, but define them before calling $.fn.ready.

Yep, if all scripts were written this way, I wouldn't worry. In reality it's not the case.

>Even if the calls are deferred, you are not guaranteed that a deferred call is going to occur after all asynchronous scripts load

The case I'm talking about does not require ALL scripts to load. Just to wait for parsing of the very script.

Changed November 19, 2010 09:39AM UTC by snover comment:3

#5673 is a duplicate of this ticket.

Changed December 23, 2010 04:49PM UTC by dmethvin comment:4

resolution: → invalid
status: newclosed

If we implemented this we'd be making a significant breaking change, because this has been in the .ready() documentation for some time:

If .ready() is called after the DOM has been initialized, the new handler passed in will be executed immediately.

So I'm going to close this ticket.