由script標籤引發了我對setTimeout非同步的思考

magic_xiang發表於2019-02-16

我們都知道在JS中setTimeout是非同步執行機制的

像setTimeout(function(){},0)這樣

如果在這條語句後面還有很多的同步任務,
它必須要等這些同步任務完成才會執行setTimeout裡面的內容

setTimeout(function(){
    不好意思,我要等for迴圈的同步任務完成才能執行
},0)
for(var i = 0; i < 99999999999; i++){
    這裡的任務完成了,才會執行setTimeout的內容
}

那麼我所發現的這個問題如下:
如程式碼所示:在body中插入兩個script標籤
<body>

<script>
    alert(`fir-script`);
    setTimeout(()=>{
        alert(`setTime`);
    },0);
</script>
<script>
    alert(`sec-script`);
    var str = `測試的字串`;
    alert(str);
</script>

</body>

這裡所用的測試字串是中文
這裡需要做3個實驗,分為A、B、C

實驗A:用比較短的字串,(有5箇中文字型)來測試,執行的彈窗結果是:
fir-script -> sec-script -> `測試字串` -> setTime


實驗B:用較長的字串,(有45000箇中文字型)來測試,結果發現了變化 執行的彈窗結果是:
fir-script ->setTime -> sec-script -> `測試字串`


實驗C:減少部分字串,(有43000箇中文字型)來測試,執行的彈窗結果是:
fir-script -> sec-script -> `測試字串` -> setTime

在其他條件沒有變化的情況下(注意:三次測試setTiemout的時間都是0)
程式執行的順序似乎和字串的長度有關
(那其實可以說是和script裡面的內容大小有關,那也可以進一步理解為和解析script內容的時間有關)

為了驗證上述結論(執行順序和解析script內容的時間有關)
在實驗B的基礎上,對setTiemout的時間進行更改,其他保持不變
如B(5)為5ms後執行,即setTimeout(()=>{alert(`setTime`);},5);
下面我們看一下結果:

B(0): fir-script -> setTime -> sec-script -> `測試字串`
B(5): fir-script -> setTime -> sec-script -> `測試字串`
B(10): fir-script ->sec-script -> `測試字串` -> setTime
B(15): fir-script -> sec-script ->`測試字串` -> setTime

從上面幾個實驗我們可以在側面看出一個現象
setTime 不會出現在 sec-script 和 `測試字串` 之間
這也反映了JS是單執行緒執行的,而且在同步任務裡,不受其他script標籤的影響

通過這個案例,發現了一些問題,總結如下:

如有其他外部指令碼(即使這些指令碼都是同步任務),像setTiemout這些非同步任務,不會等待所有同步任務執行完成
只有在同一個script標籤裡,非同步任務要等待該標籤的所有同步任務完成才會執行
因為需要去解析指令碼,而解析指令碼與執行另外的指令碼不衝突

最後的最後,如果你在嘗試過程中發現與本文的結果不相同,請把測試用的案例”加強“,如把字串加到10W,這是因為setTimeout的不確定性
歡迎大家一起來探討

相關文章