window.onload 触发时机问题

发布于 2017-12-07 21:51 阅读数 121

本文必须得到作者授权后,方可转载,摘要引流随意。
By 依韵 , From https://blog.cdswyda.com/post/20171207

本文关键点: window.onload 和 页面上 ajax 的成功回调到底哪个先触发。

答案是不确定

问题详情

之前遇到一个现象,在父页面弹出一个Dialog加载一个子页面,在onload回调中传递一个参数给子页面,子页面异步ajax成功回调中要使用这个变量。

然而出现的情况是在ajax的成功回调中大多数情况下是取不到这个在onload传来的值,但是偶尔又是可以的。

查阅此Dialog源码,以上逻辑可以进行如下简化。

父页面:

1
2
3
4
5
6
7
<iframe id="iframe" src="./iframe.html" onload="onLoad()" frameborder="0"></iframe>
<script>
    function onLoad() {
        console.log('iframe load');
        document.getElementsByTagName('iframe')[0].contentWindow.onLoad('load');
    }
</script>

子页面:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<script>
    $.ajax('./test.json').done(function (a) {
        console.log('ajax结果');
        console.log(a);
    });
    function onLoad(e) {
        console.log('window onload')
        console.log(e);
    };
</script>

由于iframe的 onload 即要加载页面的 window.onload ,因此情况可以进一步简化为一个页面中到底是 window.onload 先触发还是 ajax 的成功回调先触发。

测试代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<script>
$.ajax('./test.json').done(function (a) {
    console.log('ajax结果');
    console.log(a);
});

function onLoad(e) {
    console.log('window onload')
    console.log(e);
};
window.onload = onLoad;
</script>

这个页面除了在测试的script之前引入了jQuery没有其他代码,应该毫无疑问,是 window.onload 先触发,之后才是 ajax 的成功结果。

结果也证明是 window.onload 先触发,上面代码在浏览器运行结果为:

1
2
3
4
// window onload
// Event {}
// ajax结果
// {}

MDN上关于 window.onload 有如下解释:

The load event fires at the end of the document loading process. At this point, all of the objects in the document are in the DOM, and all the images, scripts, links and sub-frames have finished loading.

那么问题就来了,如果必然是 window.onload 先触发,那么是不可能出现最开始的问题的。

伪解释

继续修改测试代码,再加上一些东西:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<script>
    $.ajax('./test.json').done(function (a) {
        console.log('ajax结果');
        console.log(a);
    });

    function onLoad(e) {
        console.log('window onload')
        console.log(e);
    };
    window.onload = onLoad;
    // 其他代码xxx
    // 模拟一个一分钟循环
    var t1 = new Date().getTime();
    while(new Date().getTime() - t1 < 1 * 60 * 1000) {
        document.querySelectorAll('*');
    }
</script>

写入一个一分钟的循环后,结果发生了改变:

1
2
3
4
// ajax结果
// {}
// window onload
// Event {}

这么来看就奇怪了呀, ajax 的成功比 window.onload 先触发。

关于这个现象,我也没找到权威的解释。

自己给了一个“合理”的解析:

window.onload 会在当前任务队列的最后一个触发。如最开始的例子,ajax 异步,尚未给出结果,页面需要等待的所有内容已经完成,任务队列为空,因此 window.onload 触发。

而后面这个由于 ajax 后面还有很长的代码要执行,这段代码推迟了 onload 的触发,同时这段代码还未执行完成时,之前异步的ajax已经返回了结果,成功回调的代码已经被加到了任务队列,因此 ajax 回调执行后才触发 window.onload

再验证

为了进一步验证我上面的想法,那么只要保证页面资源执行完成时,ajax还没有解决即可。

因此还是上面的代码,但是将请求的内容换成一个真实接口,这个真实接口返回的数据更晚即可。

使用php暂停120s再返回结果,代码如下:

1
2
3
4
<?php
sleep(120);
echo '{"response":"two minutes later."}'
?>

结果却是如上面估计的一样:

1
2
3
4
// window onload
// Event {}
// ajax结果
// {"response":"two minutes later."}

可以说明之前的“合理”解释确实是合理的。

所以异步的 ajaxwindow.onload 到底哪个会先触发是不确定,和你js代码(或者其他onload要等待的资源,如一个图片加载很慢等)以及这个 ajax 的解决时间有关系。

因此这种情况下的传值就不能以这种方式进行,可以换成更稳妥的方式,如直接跨页面操作或者放在url进行传递。


Comments
Write a Comment