当多次调用$(function())时,jQuery的函数$(function())执行顺序是什么?

6

像这样的代码:

$(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>

当我执行上述代码时,页面的警告顺序有时是:警告1、警告2、警告3;有时是:警告1、警告3、警告2。有人能告诉我为什么吗?

1
你使用的是哪个版本的jQuery?你能在plnkr http://plnkr.co上重现这个问题吗? - guest271314
1
当我尝试执行10次时,它似乎没问题。 - Mohit Tanwani
1
这里有一个可用的 https://jsfiddle.net/3pyxfjj5/ - Weedoze
2
你的真实代码和问题中的代码完全一样吗?如果你运行问题中的代码,它是否会执行此操作?你有通过脚本加载器加载任何脚本吗? - freedomn-m
2
@freedomn-m 那种情况不可能发生。DOM准备就绪事件会在第二步后立即触发。根据定义,如果有一个尚未被解析的脚本,则DOM尚未准备就绪。 - JJJ
显示剩余10条评论
1个回答

3

在 jQuery 版本 3.1.1 中,当 document 已经加载完成后调用 .ready() 方法时,代码行 39303947 负责处理此情况。在第 3938 行,jQuery.ready 方法通过 setTimeout 被调用,但没有设置持续时间,并附有注释。

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

这可能解释了为什么window.alert('alert 3') 可能会在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 );
}

以下的 stacksnippet 应该能够复现 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>

另请参见completed函数,位于3924行。

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

请查看版本为1的plnkr http://plnkr.co/edit/C0leBhYJq8CMh7WqndzH?p=preview


编辑,更新

为了确保在.ready()中函数的执行顺序,您可以从函数调用中返回一个promise,使用.then()在单个.ready()调用内调用全局定义或之前在.ready()处理程序中定义的函数。

<!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>


@JamesThorpe:“使用setTimeout排队的事物也按顺序触发。”不确定在document DOMContentLoaded事件触发后是否仍然适用。相信如果documentwindow已经加载,则该队列是适用的。另请参见3045-3053行。 - guest271314
你可以这样做,但这是一个相当复杂的解决方法,因为这可能是 jQuery 的一个 bug :) - James Thorpe
@JamesThorpe "这可能是一个jQuery的bug :)" 再次强调,我认为这是一个设计上的问题;否则,为什么会在第3938行包含window.setTimeout( jQuery.ready )?请注意,.ready()似乎已被标记为过时 https://github.com/jquery/jquery/issues/3025。您可以在https://github.com/jquery/jquery/issues上提交问题,希望能够获得反馈,以了解您对期望结果和实际结果的解释。 - guest271314
因为此时文档已经加载完毕。如果他们没有使用setTimeout,而是立即运行回调函数,那么有时回调函数会同步运行,有时会异步运行。按设计,它总是异步运行,这正是你想要的。我认为这是一个错误,因为如果你使用相同的语法运行它们,你就看不到这种行为,而文档描述了所有调用方式都是等效的。 - James Thorpe
我认为这值得提出,尽管它有点边缘情况,因为它需要您混合使用两种语法。我仍然会将其记录为实际的jQuery错误,而不是文档错误 - 它应该按照文档所说的那样运行,而不是更改文档以适应奇怪的行为。 - James Thorpe
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接