有時候結局不是很美好,但起碼這也算是一種結局,這個系列的最后一篇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#
中也是有這樣的一個定時器,看看能不能找到些靈感,然后我就大概看了下源碼:
1 static void Main(string[] args) 2 { 3 System.Threading.Timer timer = new System.Threading.Timer(Run, "", 0, 1000 * 30 * 1); //30s 4 5 Console.Read(); 6 }
通過眼花繚亂的查找,終於明白,原來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系列的持續關注,也祝大家在新的一年工作順利~~~