這個系列的文章名爲「JavaScript 進階」,內容涉及JS中容易忽略可是頗有用的,偏JS底層的,以及複雜項目中的JS的實踐。主要來源於我幾年的開發過程當中遇到的問題。小弟第一次寫博客,寫的很差的地方請諸位斧正,以爲還有一些閱讀價值的請幫忙分享下。這個「JavaScript 進階」是一個系列文章,請你們鼓勵鼓勵,我儘快更新。另外,若是你有比較好的話題,也能夠在下面評論,咱們一塊兒研究提升。javascript
多線程編程相信你們都很熟悉,好比在界面開發中,若是一個事件的響應須要較長時間,那麼通常作法就是把事件處理程序寫在另一個線程中,在處理過程當中,在界面上面顯示相似進度條的元素。這樣界面就不會卡住,而且可以顯示任務執行進度。記得剛開始作前端的時候,老闆交代在界面上面作一個定時器,每秒更新用戶的在線時間。當時擁有Java和C++開發經驗的我自信滿滿的說我加一個線程就能夠分分鐘搞定了。因此查閱文檔,發現setTimeout和setInterval能夠很方便的實現該功能。那時候我就認爲這就是JS中的多線程。setTimeout至關於啓動一個線程,等待一段時間後執行函數,setInterval則是在另外的一個線程中,每隔一段時間執行函數。這個觀念在個人頭腦中存在了一年左右,直到遇到了這樣的一個問題。html
測試人員發現一個按鈕的點擊響應時間較長,在響應過程當中,界面卡住了,我檢查代碼發現代碼中作了這樣的事情。前端
- $("#submit").on("click", function() {
- bigTask(); // 這個函數須要較長時間來執行
- })
因此我想很簡單啊,把這個函數放在另外的一個線程執行就行了啊,因此代碼改爲了這樣, 覺得能夠輕鬆解決問題。可是事實上發現毫無用處,界面仍是原來同樣的行爲,點擊按鈕以後卡住了幾秒。
- $("#submit").on("click", function() {
- setTimeout(function() {
- bigTask()
- }, 0)
- })
這個問題我百思不得其解,最後多方查閱資料才明白了以下的內容:html5
想證實setInterval和setTimeout不是多線程很簡單,你能夠試試這樣一段代碼java
不出意外,你的瀏覽器沒法響應了,頁面上面的按鈕不能點,Gif也不能動,那個alert確定也出不來。這是爲何呢?web
爲了解釋上面的問題,咱們來深刻解析一下瀏覽器。瀏覽器中有三個常駐的線程,分別是JS引擎,界面渲染,事件響應。因爲這三個線程同時要訪問DOM樹,因此爲了線程安全,瀏覽器內部須要作互斥:當JS引擎在執行代碼的時候,界面渲染和事件響應兩個線程是被暫停的。因此當JS出現死循環,瀏覽器沒法響應點擊,也沒法更新界面。如今的瀏覽器的JS引擎都是單線程的,儘管多線程功能強大,可是線程同步比較複雜,而且危險,稍有不慎就會崩潰死鎖。單線程的好處沒必要考慮線程同步這樣的複雜問題,簡單而安全。下面的一幅圖來簡要說明JS引擎的執行流程:編程
JS引擎基於事件來執行代碼。事件響應線程在接到事件後,把響應的代碼放到JS引擎的隊列中,JS引擎按順序執行代碼。在JS引擎沒有代碼能夠執行的時候,好比圖中藍色方框的間隙中,事件線程和渲染線程得以有機會運行。基於這些信息,可以的出下面的結論瀏覽器
看到這邊相信各位應該對JS的單線程以及setTimeout,setInterval的本質有所瞭解了,那麼咱們再繼續討論下一個問題,異步Ajax。上文說了,JS是單線程的,當一個函數執行的時候,JS引擎會鎖住DOM樹,其餘事件的響應代碼只能在隊列中等待,而且此時頁面卡死。那麼異步Ajax是怎麼回事呢?一個經常使用的開發實踐就是發起一個異步的Ajax,界面顯示一個進度條樣式的Gif,說好的單線程呢?事實上異步Ajax確實用了多線程,只是Ajax請求的Http鏈接部分由瀏覽器另外開了一個線程執行,執行完畢以後給JS引擎發送一個事件,這時候異步請求的回調代碼得以執行。它的執行流程是這樣的:安全
Http請求的執行在另一個線程中,因爲這個線程並不會操做DOM樹,因此是能夠保證線程安全的。發起Ajax請求和回調函數中間是沒有JS執行的,因此頁面不會卡死。多線程
在HTML5中,引入了Web Worker這個概念。它可以在另一個線程中執行計算密集的JS代碼而不引發頁面卡死,這是真正的多線程。然而爲了保證線程安全,Worker中的代碼是不能訪問DOM的。其具體使用方法在此不做贅述,請參考:http://www.w3school.com.cn/html5/html_5_webworkers.asp
結合上面的分析,總結出出下面的一些實踐供各位參考。