前段時間剛看完《JS忍者祕籍》,雖然說是15年出版的,有些東西是過期了,但像對原型鏈、閉包、正則、定時器之類的機制倒是不會過期的,裏面不少東西都講的很細,仍是值得一讀的,本文將對這本書中對定時器機制的部分進行詳細的解析,若是喜歡的話能夠點波贊/關注,支持一下,但願你們看完本文能夠有所收穫。ajax
我的博客瞭解一下:obkoro1.compromise
閱讀本文以前,推薦先閱讀Js 的事件循環(Event Loop)機制以及實例講解這篇文章來理解背後發生的事情,本文對事件循環機制不會很仔細的講解。瀏覽器
因爲JS的單線程特性,定時器提供了一種跳出單線程限制的方法,即讓一段代碼在必定毫秒以後,再異步執行。markdown
直接引用忍者祕籍中的圖片:閉包
setTimeout
和setInterval
的時候最好將其賦值給一個變量,以便取消定時器。Vue
的時候,setTimeout
和setInterval
的this指向的是window對象,訪問不到組件數據以及方法。Vue
的時候,路由跳轉並不會銷燬setInterval
,可是組件已經銷燬了,這會致使問題。setTimeout
/setInterval
沒法保證準時執行回調函數的。setInterval
調用有可能會被廢棄以及setInterval
的連續執行第三點和第四點的解決方法能夠參考我以前寫的Vue 實踐過程當中的幾個問題。異步
接下來要講的是第五點和第六點,這兩點是最重要,也是本文要重點解析的內容。函數
下面來看忍者祕籍中的栗子:oop
讓咱們看看這裏發生了什麼事情:post
setTimeout
以及setInterval
,setTimeout
先設定。同時發生了這麼多事情,因爲js的單線程特性,當線程正在執行狀態,有異步事件觸發時,它就會排隊,而且在線程空閒時才進行執行。學習
這裏的異步事件包括:鼠標單擊,定時器觸發,ajax請求、promise等事件。
複製代碼
讓咱們回到栗子中:
栗子中首先有一個18毫秒的代碼塊要執行,在這18毫秒中只能執行這段代碼塊,其餘事件觸發了以後只能排隊等待執行。
在代碼塊還在運行期間,第6毫秒的時候,發生了一個鼠標單擊事件,以及第10毫秒時的setTimeout
和setInterval
兩個處理程序,這三個事件不能當即執行,而是被添加到等待執行的隊列中。
18毫秒的時候代碼塊結束執行,有三個任務在排隊等待執行,根據先進先出的原則,此時會先執行click事件,setTimeout
和setInterval
將繼續排隊等待執行。
在click事件執行時,第20毫秒處,第二個setInterval
也到期了,由於此時已經click事件佔用了線程,因此setInterval
仍是不能被執行,而且由於此時隊列中已經有一個setInterval
正在排隊等待執行,因此這一次的setInterval
的調用將被廢棄。
瀏覽器不會對同一個setInterval處理程序屢次添加到待執行隊列。
setTimeout
/setInterval
沒法保證準時執行回調函數click事件在第28毫秒處結束執行,有兩個任務(setTimeout
和setInterval
)正在等待執行,遵循先進先出的原則,setTimeout
早於setInterval
設定,因此先執行setTimeout
。
so:咱們指望在第10毫秒處執行的setTimeout
處理程序,最終會在第28毫秒處纔開始執行,這就是上文提到的setTimeout
/setInterval
沒法保證準時執行回調函數。
在30毫秒處,setInterval
又觸發了,由於隊列中已經有setInterval
在排隊,因此此次的觸發又做廢了。
setTimeout
執行結束,在第36毫秒處,隊列中的setInterval
處理程序纔開始執行,setInterval
須要執行6毫秒。
在第40毫秒的時候setInterval
再次觸發,由於此時上一個setInterval
正在執行期間,隊列中並無setInterval
在排隊,此次觸發的setInterval
將進入隊列等候。
因此:setInterval
的處理時長不能比設定的間隔長,不然setInterval
將會沒有間隔的重複執行
第42毫秒的時候,第一個setInterval
結束,而後隊列中的setInterval
當即開始執行,在48毫秒的時候完成執行。而後50毫秒的時候再次觸發setInterval
,此時沒有任務在排隊,將會當即執行。
上文說了,setInterval
的處理時長不能比設定的間隔長,不然setInterval
將會沒有間隔的重複執行。
可是對這個問題,不少狀況下,咱們並不能清晰的把控處理程序所消耗的時長,爲了咱們能按照必定的間隔週期性的觸發定時器,忍者祕籍中提供了下面這種使用方法:
// 實際上我不止在忍者祕籍中見過,在不少地方都見過這種技術。
setTimeout(function repeatMe(){
// do something
setTimeout(repeatMe,10);
// 執行完處理程序的內容後,在末尾再間隔10毫秒來調用該程序,這樣就能保證必定是10毫秒的週期調用
},10)
複製代碼
事實上,關於任務隊列並非只有簡單的排隊而已,忍者祕籍中提到爲了方便,使用了這個概念,若是但願能更清晰的瞭解背後的機制,再次推薦閱讀一下:Js 的事件循環(Event Loop)機制以及實例講解,
這上面全部一切,都是由js單線程特性致使的,因此會有事件排隊、先進先出、setInterval調用被廢棄、定時器沒法保證準時執行回調函數以及出現setInterval的連續執行,時刻記住這一特性,不少關於事件執行順序的問題都能想的通,而且找出解決方法。
我的blog and 掘金我的主頁,如需轉載,請放上原文連接並署名。碼字不易,感謝支持!本人寫文章本着交流記錄的心態,寫的很差之處,不撕逼,可是歡迎指點。
若是喜歡本文的話,歡迎關注個人訂閱號,漫漫技術路,期待將來共同窗習成長。
以上2018.6.17
JS忍者祕籍第8章:馴服線程和定時器