本文首發我的部落格 – blog.cdswyda.com,轉載務必保留作者和出處,以便追溯和錯誤修正。
本文關鍵點: window.onload
和 頁面上 ajax
的成功回撥到底哪個先觸發。
答案是不確定。
問題詳情
之前遇到一個現象,在父頁面彈出一個Dialog載入一個子頁面,在onload回撥中傳遞一個引數給子頁面,子頁面非同步ajax成功回撥中要使用這個變數。
然而出現的情況是在ajax的成功回撥中大多數情況下是取不到這個在onload傳來的值,但是偶爾又是可以的。
查閱此Dialog原始碼,以上邏輯可以進行如下簡化。
父頁面:
<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>
複製程式碼
子頁面:
<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
的成功回撥先觸發。
測試程式碼:
<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
先觸發,上面程式碼在瀏覽器執行結果為:
// 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 先觸發,那麼是不可能出現最開始的問題的。
偽解釋
繼續修改測試程式碼,再加上一些東西:
<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>
複製程式碼
寫入一個一分鐘的迴圈後,結果發生了改變:
// ajax結果
// {}
// window onload
// Event {}
複製程式碼
這麼來看就奇怪了呀, ajax
的成功比 window.onload
先觸發。
關於這個現象,我也沒找到權威的解釋。
自己給了一個“合理”的解析:
window.onload
會在當前任務佇列的最後一個觸發。如最開始的例子,ajax
非同步,尚未給出結果,頁面需要等待的所有內容已經完成,任務佇列為空,因此 window.onload
觸發。
而後面這個由於 ajax
後面還有很長的程式碼要執行,這段程式碼推遲了 onload
的觸發,同時這段程式碼還未執行完成時,之前非同步的ajax已經返回了結果,成功回撥的程式碼已經被加到了任務佇列,因此 ajax
回撥執行後才觸發 window.onload
。
再驗證
為了進一步驗證我上面的想法,那麼只要保證頁面資源執行完成時,ajax還沒有解決即可。
因此還是上面的程式碼,但是將請求的內容換成一個真實介面,這個真實介面返回的資料更晚即可。
使用php暫停120s再返回結果,程式碼如下:
<?php
sleep(120);
echo `{"response":"two minutes later."}`
?>
複製程式碼
結果卻是如上面估計的一樣:
// window onload
// Event {}
// ajax結果
// {"response":"two minutes later."}
複製程式碼
可以說明之前的“合理”解釋確實是合理的。
所以非同步的 ajax
和 window.onload
到底哪個會先觸發是不確定,和你js程式碼(或者其他onload要等待的資源,如一個圖片載入很慢等)以及這個 ajax
的解決時間有關係。
因此這種情況下的傳值就不能以這種方式進行,可以換成更穩妥的方式,如直接跨頁面操作或者放在url進行傳遞。