6

Code like this:

$(window.document).ready(function () {
    window.alert('alert 1');
});

$(function () {
    window.alert('alert 2');
});

$(function () {
   window.alert('alert 3');
});
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo2</title>
    <script src="jquery-3.1.1.js"></script>
    <script src="demo2.js"></script>
</head>
<body>

</body>
</html>

when i execute the above code, the page' alert order sometimes is: alert 1, alert 2, alert 3, and sometimes is: alert 1, alert 3, alert 2. could anyone tell my why?

guest271314
  • 1
  • 15
  • 104
  • 177
MayGodBlessYou
  • 649
  • 6
  • 19
  • 1
    Which version of jQuery did you try with? Can you reproduce issue at plnkr http://plnkr.co? – guest271314 Sep 30 '16 at 07:33
  • 1
    It seems ok when I tried to execute it 10 times. – Mohit Tanwani Sep 30 '16 at 07:35
  • http://stackoverflow.com/questions/3934570/order-of-execution-of-jquery-document-ready – freedomn-m Sep 30 '16 at 07:36
  • 1
    Here is a working https://jsfiddle.net/3pyxfjj5/ – Weedoze Sep 30 '16 at 07:36
  • http://stackoverflow.com/questions/10883786/jquery-enforce-order-of-execution-of-document-ready-calls – freedomn-m Sep 30 '16 at 07:36
  • 1
    Is your real code exactly like the code in the question? If *you* run the code in the question does it do this? Do you have any scripts loaded via script-loaders? – freedomn-m Sep 30 '16 at 07:38
  • 1
    With this exact code, the order is always the same. If that's not true in the actual code, the problem is somewhere else that's not shown here (different files loaded asynchoronously?) – JJJ Sep 30 '16 at 07:38
  • 1
    [The docs](https://api.jquery.com/ready/) say _"When multiple functions are added via successive calls to this method, they run when the DOM is ready in the order in which they are added."_, so if you really _are_ seeing this behaviour, you've found a bug in jQuery. – James Thorpe Sep 30 '16 at 07:38
  • If the document is already ready, it will run the function immediately. It could be race conditions where you 1 add a handler (it waits) 2 dom is ready 3 add another handler (it runs immediately) 4 dom ready event fires and runs 1 – freedomn-m Sep 30 '16 at 07:39
  • 2
    @freedomn-m That situation can't happen. The DOM ready event fires exactly after step 2. By definition if there's a script that has not yet been parsed, the DOM isn't ready. – JJJ Sep 30 '16 at 07:41
  • @Jack See http://stackoverflow.com/help/mcve – guest271314 Sep 30 '16 at 07:52
  • @JJJ thanks for clarifying. It was pretty much the only scenario I could come up with, hence the "it could be [but is unlikely]". – freedomn-m Sep 30 '16 at 07:54
  • @Jack can you provide a *reproducible* version? Using one of the many online test sites such as jsfiddle.net or pinkr.co. [mcve] – freedomn-m Sep 30 '16 at 07:55
  • @freedomn-m http://plnkr.co/edit/C0leBhYJq8CMh7WqndzH?p=preview – guest271314 Oct 01 '16 at 15:09
  • @JamesThorpe May not be a bug, but by design of authors. `.ready()` call appears to be wrapped within `setTimeout` without a duration set when `.ready()` is called after `document` has loaded, that is `DOMContentLoaded` and `window`s `load` event have fired. – guest271314 Oct 01 '16 at 15:57

1 Answers1

3

At lines 3930 through 3947 jQuery version 3.1.1 handles .ready() being called after document has already loaded. At line 3938 jQuery.ready is called inside of setTimeout without a duration set with attached comment

// Handle it asynchronously to allow scripts the opportunity to delay ready

which would explain how window.alert('alert 3') could potentially be called before window.alert('alert 2')


// Catch cases where $(document).ready() is called
// after the browser event has already occurred.
// Support: IE <=9 - 10 only
// Older IE sometimes signals "interactive" too soon
if ( document.readyState === "complete" ||
    ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {

    // Handle it asynchronously to allow scripts the opportunity to delay ready
    window.setTimeout( jQuery.ready ); // Line 3938

} else {

    // Use the handy event callback
    document.addEventListener( "DOMContentLoaded", completed );

    // A fallback to window.onload, that will always work
    window.addEventListener( "load", completed );
}

The following stacksnippet should reproduce result described by OP

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Demo2</title>
  <script src="https://code.jquery.com/jquery-3.1.1.js"></script>
  <script>
    $(window.document).ready(function() {
      window.alert('alert 1');
    });

    $(function() {
      window.alert('alert 2');
    });

    $(function() {
      window.alert('alert 3');
    });
  </script>
</head>

<body>

</body>

</html>

See also completed function at Line 3924

// The ready event handler and self cleanup method
function completed() {
    document.removeEventListener( "DOMContentLoaded", completed );
    window.removeEventListener( "load", completed );
    jQuery.ready();
}

See plnkr http://plnkr.co/edit/C0leBhYJq8CMh7WqndzH?p=preview at version 1


Edit, Updated

To ensure the order of execution of functions at .ready() you can return a promise from the function calls, use .then() inside single .ready() call to call functions defined globally or previously within .ready() handler.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Demo2</title>
  <script src="https://code.jquery.com/jquery-3.1.1.js"></script>
  <script>
    function ready1(wait, index) {
      // do stuff
      return new Promise(resolve => {
          setTimeout(() => {
            window.alert('alert ' + index);
            resolve(index)
          }, wait)
        })
        .then((i) => console.log(i))
    }

    function ready2(wait, index) {
      // do stuff
      return new Promise(resolve => {
          setTimeout(() => {
            window.alert('alert ' + index);
            resolve(index)
          }, wait)
        })
        .then((i) => console.log(i))
    }

    function ready3(wait, index) {
      // do stuff
      return new Promise(resolve => {
          setTimeout(() => {
            window.alert('alert' + index);
            resolve(index)
          }, wait)
        })
        .then((i) => console.log(i))
    }
    $().ready(function() {
      ready1(3000, 0) 
      .then(function() {
        return ready2(1500, 1) 
      })
      .then(function() {
        return ready3(750, 2) 
      });
    })
  </script>
</head>

</html>
guest271314
  • 1
  • 15
  • 104
  • 177
  • Things queued up with `setTimeout` also fire in order. I don't have time to look at the spec right now, but it's in there. OPs code must be more complicated than appears here. – James Thorpe Oct 01 '16 at 16:01
  • @JamesThorpe _"Things queued up with `setTimeout` also fire in order."_ Not sure if this is the case after `document` `DOMContentLoaded` event has fired. Believe the queue is applicable if `document`, `window` has not already been loaded. See also lines 3045-3053 – guest271314 Oct 01 '16 at 16:05
  • But the event _can't_ fire during OPs code. There are 3 simple calls to `ready` with no chance of anything else happening in between. Either the doc is ready, or it isn't. Either way, all 3 callbacks will get added to a queue and executed sequentially. If the OP really is seeing this behaviour, they've simplified their code too much and it's hiding the true cause. – James Thorpe Oct 01 '16 at 16:09
  • @JamesThorpe plnk http://plnkr.co/edit/C0leBhYJq8CMh7WqndzH?p=preview demonstrates the behaviour described by OP. Only the first `.ready()` call appears to place functions to be called in a queue. Following `completed` function being called `.ready()` appears to be called within `setTimeout` – guest271314 Oct 01 '16 at 16:10
  • Hmm that is curious... if you remove the `window.document` and just use the same syntax for the first one as for the other two, the problem disappears. Does seem to be a bug in jQuery between the different styles of handler, given that they're supposed to do the same thing. – James Thorpe Oct 01 '16 at 16:21
  • @JamesThorpe You can alternatively chain `.done()` with an array of functions to be called in sequential order to `.promise()` chained to single `.ready()` call to catch only jQuery promise resolved at `.ready()`. See updated post. – guest271314 Oct 01 '16 at 16:26
  • You can, but it's quite a complex workaround for what is arguably a jquery bug :) – James Thorpe Oct 01 '16 at 16:53
  • @JamesThorpe _"what is arguably a jquery bug :)"_ Again, do believe it is a bug, but by design; else, why would `window.setTimeout( jQuery.ready )` be included at Line `3938`? Note, `.ready()` appears to be slated for deprecation https://github.com/jquery/jquery/issues/3025. You can file an issue at https://github.com/jquery/jquery/issues to, hopefully, get feedback as to your interpretation of expected result and actual result at stacksnippets – guest271314 Oct 01 '16 at 16:58
  • @JamesThorpe Though, granted `

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

    ` at https://github.com/jquery/api.jquery.com/commit/ba2abaa99d0236e8360efe9e76e778984c91b715 is potentially misleading, where the call appears to actually be `window.setTimeout( jQuery.ready ); // see Line 3938 at 3.1.1`?
    – guest271314 Oct 01 '16 at 17:04
  • Because the document is already loaded at that point. If they didn't `setTimeout`, and instead ran the callback immediately, you'd have the case where sometimes callbacks are run synchronously and sometimes asynchronously. By design, it _always_ runs them async, which is what you want. I'm arguing that it's a bug because you don't see the behaviour if you run them all with the same syntax, when the docs describe all of the various ways of calling it to be equivalent. – James Thorpe Oct 01 '16 at 17:04
  • @JamesThorpe That is a good point. And should be brought to attention of authors of documentation. Do you believe that issue is directly connected to present Question? – guest271314 Oct 01 '16 at 17:09
  • I think it's worth raising, though it's somewhat of an edge case as it requires you to mix the two syntaxes. I'd log it as an actual jQuery bug rather than a docs bug still though - it ought to behave as the docs says it does, rather than altering the docs to suit the strange behaviour. – James Thorpe Oct 01 '16 at 17:10
  • No, just on mobile at the moment so not really in a position to formulate a proper report. Probably won't be able to until Monday – James Thorpe Oct 01 '16 at 17:21
  • @JamesThorpe The report does not have to be elaborate. You can re-post http://stackoverflow.com/questions/39786050/jquerys-function-function-s-execute-order-when-the-function-called-mo/39808569#comment66909920_39808569 , create plnk with syntax you mentioned, and link to this Question. Viewers of issues should get gist of what is being presented. – guest271314 Oct 01 '16 at 17:22
  • @guest271314 Do you think is it a bug of jQuery? – MayGodBlessYou Oct 05 '16 at 09:51
  • @Jack _"Do you think is it a bug of jQuery?"_ Not certain. Appears to be by design. You can post an issue at [Issues - jquery/jquery](https://github.com/jquery/jquery/issues) – guest271314 Oct 05 '16 at 23:15
  • @Jack See updated post. Substituted using `.then()` for `$.when()`, `.done()` to call functions in sequential order. – guest271314 Oct 06 '16 at 05:23