一、前言
由于JavaScript程序為單線程,因此在執行長時間的操作時(如循環和遞歸操作)到導致UI線程長期被阻塞,無法響應用戶操作請求(如點擊按鈕等),讓用戶體驗大打折扣。于是想到將一個長時間操作切片成N個小操作并異步執行,例如jsDeferred中的 Deferred.repeat函數 就提供這樣的解決辦法,其實質就是通過 setTimeout事件 或 script元素 的 onerror/onload事件 來異步調用這些小操作,從而盡早釋放UI線程。
從IE10開始引入了setImmediate接口來代替setTimeout來完成上述功能,下面將記錄該接口的資訊,由于內容會涉及到event loop、調用棧等知識,而我對相關內容了解仍不全面,因此下面的內容若有紕漏請各位指正,謝謝!
二、同步和異步調用
由于JavaScript是通過異步調用來盡早釋放UI線程,因此我們先要了解同步和異步執行的具體含義:
任務的執行實質上分為兩步:①.執行,②.獲取執行結果。
同步執行:執行后等待直到獲取執行結果;
異步執行:執行后不等待,而是通過一系列手段(輪詢、事件監聽和event loop等)獲取執行結果,而在執行后和獲取結果前的那段時間可以介入其他任務操作。
二、setTimeout(handler, 0)的問題
由于setTimeout存在時間精度,因此setTimeout(handler,0)中setTimeout事件插入事件隊列的延時必定大于0ms,而handler的執行延時則更大了。具體為IE5~8和不插電源的IE9的時間精度為15.6ms,插電源的IE9和其他瀏覽器則為4ms。
經微軟和Chrome團隊實驗所得降低時間精度將會大大縮短筆記本的續航時間,也是就說更耗電,因此即使瀏覽器廠商有能力縮短時間精度,但基于多方面的考慮,依然保持上述的精度值。
三、setImmediate接口
對于通過異步執行的手段對任務切片,由于UI線程得到釋放從而提高用戶體驗,但相對于采用同步執行,整體的任務執行時間較被拉長,因此我們希望切片的小操作越快執行越好。而setImmediate接口則是為此而生的。
setImmediate(handler) 并不像 setTimeout(handler, 0) 由event loop檢測系統時間是否到點然后向事件隊列插入一個事件,然后調用事件的回調方法handler。而是監控UI線程的調用棧,一旦調用棧為空則將handler壓棧。
理論上通過setImmediate執行異步調用的延時一定比通過setTimeout的短,但事實又是如何呢?
我在IE11上操作測試,setTimout的延時為4ms左右,而setImmediate的延時為2ms左右。
注意:
1. 通過setImmediate的異步調用的延時不是0ms哦!
2. 而且有時候setImmediate的延時比setTimeout的多1~2ms哦!
3. 而且setImmediate和setTimeout的延時均比img元素onerror事件的延時長哦!
推測:
1. 對于setImmediate的延時有時比setTimeout的要長,由于setImmediate要先監控調用棧,若調用棧為空才壓棧,那么在壓棧之前event loop已經將setTimeout事件的回調函數壓棧了。
2. 對于兩者均比img元素onerror事件的長,由于設置img.src后馬上發起請求(不一定為網絡IO)當加載失敗時onerror事件則會比setTimeout事件先加入事件隊列。
四、總結
本文內容紕漏之處請各位指正,謝謝!
尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/4151595.html ^_^肥仔John
五、參考
http://developer.51cto.com/art/201109/292720.htm
http://msdn.microsoft.com/library/ie/hh920766.aspx
https://github.com/yuzujs/setimmediate
http://www.nczonline.net/blog/2011/09/19/script-yielding-with-setimmediate/
文章列表