Bug Tracker

Opened 9 years ago

Closed 9 years ago

Last modified 8 years ago

#7366 closed enhancement (invalid)

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

Reported by: taka.atsushi@… 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.

Change History (4)

comment:1 Changed 9 years ago by snover

Keywords: needsreview added
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.

Last edited 9 years ago by snover (previous) (diff)

comment:2 Changed 9 years ago by taka.atsushi@…

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.

comment:3 Changed 9 years ago by snover

#5673 is a duplicate of this ticket.

comment:4 Changed 9 years ago by dmethvin

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.

Note: See TracTickets for help on using tickets.