有時候結局不是很美好,但起碼這也算是一種結局,這個系列的最後一篇settimeout,這是一個讓人困惑的函式,也是我一直在吐槽JS的原因,我們看不到JS的原始碼,setimeout同樣也是,從始到終都是黑盒子的使用。
一:settimeout單執行緒的質疑?
所有的教科書都在說js是單執行緒模型,也說settimeout的執行函式會丟給js的內部執行佇列,這其中還包括onlick事件以及一些xhr的回呼函式。乍一看貌似是這麼一回事,既然要排隊嘛,那肯定是FIFO的原則了,誰也無法保證準確的定時觸發,就算精確的觸發的,也不能保證在執行佇列中馬上執行,因為要排隊,如果我設定了5s觸發,所以時間一定會在5s 以上,問題就出現在這裡,這個5s觸發的機制是什麼樣的???誰能告訴我呢????既然js是單執行緒的,難道是js會不停的輪訓“執行佇列”嗎?問執行函式5s時間到了嗎?5s時間到了嗎???我想JS肯定不會這麼傻乎乎做這麼個“內旋”操作,因為如果我設定的觸發時間是1年呢?難道還要內旋1年麼???而且這種拉樣式是相當耗費CPU時間的,那為了儘量節省CPU的時間,是否會有其他執行緒來輔助setttimeout來做這個5s的機制呢?然後5s時間到了將setimeout中的執行函式推入js的內部執行佇列呢?到底合理的方式會是怎麼樣的?
二:在System.Threading.Timer中尋找靈感
因為setimeout的閉源,我看不到settimeout內部到底怎麼做到5s觸發的機制,非常遺憾,我也只能去找類似語言中的Timer機制,還好在C#中也是有這樣的一個定時器,看看能不能找到些靈感,然後我就大概看了下原始碼:
static void Main(string[] args)
{
System.Threading.Timer timer = new System.Threading.Timer(Run, “”, 0, 1000 * 30 * 1); //30s
Console.Read();
}
透過眼花繚亂的查詢,終於明白,原來Timer僅僅是對一個Win32中CreateAppDomainTimer函式的封裝,真是他們的坑貨,先讓你眼見為實。
其中的dueTime也就是我的Timer建構式中的period引數,這裡也就是30s,然後定時觸發AppDomainTimerCallback函式,一段邏輯後再呼叫Fire方法觸發我們的CallCallback函式。
其實我們看到原始碼之後,發現Timer計時器其實是個假的,只是封裝了Win32函式,並且也沒有做到完完全全的30s,這是因為callback()是在呼叫AppDomainTimerCallback函式之後觸發的,這些邏輯也是需要耗費時間的,包括win32回呼的誤差,那現在有什麼靈感呢?既然C#的多執行緒採用工作執行緒去跑都有誤差,那你單執行緒的settimeout又何德何能呢?那更不用談用主執行緒去輪訓settimeout這個很不現實的東西。
三:最後的一點猜測
透過對C#中的Timer原理的一些理解,我覺得settimeout應該是這樣的,這其中的5s機制應該是丟給瀏覽器核心執行緒了,由瀏覽器核心執行緒去實現這個5s的機制,如果5s時間到了,核心會將function函式塞給js的“內部執行佇列”,由js主執行緒空閑的時候去得以執行。畢竟瀏覽器執行緒還是有很多的,比如下麵的IE9:
好了,不說了,說的再多也是猜測,結局並不完美,感謝大家對javascript系列的持續關註,也祝大家在新的一年工作順利~~~
來自:一線碼農的部落格
連結:http://www.cnblogs.com/huangxincheng/p/4199734.html