最近在遇到一個問題:HTML頁面中的頁面無法刷新,只能在底層全部處理完成后才能進行頁面刷新。在里面已經采用SetTimeout進行了處理,但是明顯沒有達到預期的效果。
主要的原因是對SetTimeout這個函數並未清楚,對Javascript單線程也沒有清晰的認識。
瀏覽器中的線程介紹
通常一個瀏覽器會至少存在三個線程:JS引擎線程(用於處理JS)、GUI渲染線程(用於頁面渲染)、瀏覽器時間觸發線程(用於控制交互)。
而因為JS可以操作DOM元素,進而會影響到GUI的渲染結果,因此JS引擎線程與GUI渲染線程是互斥的。也就是說當JS引擎線程處於運行狀態時,GUI渲染線程將處於凍結狀態。
JS引擎是基於事件驅動,采用的是單線程運行機制。即JS引擎會只會順序的從任務列表中取任務,並執行。
SetTimeout/SetInternal
其中SetTiemout:在指定的毫秒數后調用指定的代碼段;SetInternal:在指定的時間間隔內(ms)循環調用指定的代碼段。這兩個函數內都涉及到時間計數器,也就是都涉及到一個類似與MFC定時器。JS引擎本身就只能單線程運行,因此定時器需要由其他的外部線程來啟動。所以對JS引擎而言,定時器線程可以被視為異步線程。但當定時器時間到達后,所觸發的事件則必須在任務列表中排隊,等候JS引擎的處理。
關於setTimeout下面有一個例子,可以幫助深入理解:
setTimeout(function () { while (true) { } }, 1000); setTimeout(function () { alert('end 2'); }, 2000); setTimeout(function () { alert('end 1'); }, 100); alert('end');
執行的結果是彈出‘end’‘end 1’,然后瀏覽器假死,就是不彈出‘end 2’。也就是說第一個settimeout里執行的時候是一個死循環,這個直接導致了理論上比它晚一秒執行的第二個settimeout里的函數被阻塞,這個和我們平時所理解的異步函數多線程互不干擾是不符的。
引用的網頁:
例子:
http://www.cnblogs.com/jeffwongishandsome/archive/2011/06/13/2080145.html
w3c給出的SetTimeout/SetInternal的函數介紹及使用示例
http://www.w3school.com.cn/htmldom/met_win_settimeout.asp
http://www.w3school.com.cn/htmldom/met_win_setinterval.asp
JS的多線程介紹(里面畫了一張介紹任務隊列機制的原理圖,圖畫的清楚,解釋的也很清楚)
http://blog.csdn.net/nx8823520/article/details/7513098