1. 程式人生 > >試著講清楚:js代碼運行機制

試著講清楚:js代碼運行機制

監聽器 作用 網絡 效果 千萬 了解 請求 概念 使用

一、 js運行機制

js執行引擎

經常看文章的說到js是帶線程的,其實這個說法非常的模糊,準確的是js執行引擎是單線程的,js執行引擎就是js代碼的執行器,有了這個概念就可以下來說說js是如何運行的了。

js代碼如何運行?

在js代碼執行的時候,js的代碼是按照順序執行的,從上到下,這個時候是同步的,不過,有幾個例外:

  • 異步的網絡請求
  • 事件綁定、事件監聽器
  • 時間觸發函數

我們模擬一下,js引擎遇到這三類代碼的情況:

  1. js執行的好好的,正在順序執行代碼,這個時候呢,遇到了異步的網絡請求的代碼,這個時候,直接js代碼調用之後,就繼續執行之後的js代碼了,並沒有等待網絡請求的結果,js代碼執行是單線程的(不考慮h5),那麽肯定異步網絡請求調用之後,肯定不是js執行引擎管理了,問題一:誰在等待,誰之後通知js執行引擎結果,畢竟是js代碼就得js引擎是執行。

  2. 有了第一個問題,我們已經知道肯定有其它的東西參與了上面的事情了,先放著,之後呢js有歡樂的往下執行,遇到了事件綁定和事件監聽函數,哦,這個js調用之後,也是繼續往下走的,並沒有調用那些函數之類的,我們知道,事件函數只有在事件觸發的時候會起作用,js代碼js執行引擎執行。那麽問題二:誰把事件關聯的函數保存的,以便接收事件觸發選擇正確的事件處理函數,通知js執行引擎?

  3. js在遇到setTimeout和setInternal函數的時候,也是不直接執行,然後處理之後的代碼的,那麽問題三:誰在監視時間流逝選擇合適的觸發函數通知js引擎執行的?

以上3點是在自己學習js的運行機制和js執行引擎的時候,遇到了3個問題,那麽3個問題其中的js執行js代碼,但是監視肯定不是js執行引擎,答案如下:

  1. 異步網絡請求線程
  2. 事件觸發線程
  3. 定時觸發器線程

以上3個線程是獨立於js引擎之外,幫助處理這3類代碼的,所以啊,千萬不要認為js引擎什麽都做,加上gui渲染線程,js執行引擎線程,5個線程齊了。

以上我們了解了js執行的代碼和3個額外的線程協助下,js執行代碼構建了完備的環境了,但是js執行引擎是如何執行這3類線程給的函數的,答案是隊列(暫且叫做Message Queue),3個線程選擇好了需要執行的函數之後,會進行包裝成一個結構(消息),放到隊列裏面,js執行玩代碼之後,會循環的在這個隊列裏面取消息,取到了,那麽就執行,取不到了就等待。

以上的一個執行結構,那麽我們就可以得出一個結論,js在遇到3類代碼的時候,一定滯後於同步代碼的,因為同步代碼執行完成之後,js才會從隊列中取一條消息,來執行,並且執行完成之後才會再取一條。

以上也就是為什麽時間處理函數總是在同步代碼之後執行的原因,異步網絡請求的回調函數也在同步代碼執行完成之後調用的原理,就是因為這3類函數被3類線程放到了隊列裏了,而隊列裏面的代碼在js執行完同步代碼之後才能執行。

setTimeout為什麽不能恰到好處的執行呢,這是因為定時觸發器線程只是在時間到了之後,把應該執行的函數進行封裝放到隊列裏面而已,具體什麽時候執行還得看之前隊列含有多少消息沒有被處理。

二、gui渲染線程與js執行引擎的交互機制

上面說了3個執行線程與js執行引擎的交互,這個基本上沒有問題了,這下說說渲染與js代碼之間的交互。

在寫js代碼初期,肯定遇到過js代碼執行性能問題,然後頁面直接就不動彈了,這個時候其實可以得出來一個結論,js執行的時候,渲染線程是阻塞的,之後查了資料,發現這個定義更準確的說法是:js執行引擎的線程和gui渲染線程是互斥的。

這也能解釋為什麽js執行時間長後渲染不動的問題了。那麽一個新的問題來了,如果是這樣的話,那麽渲染引擎肯定就不能渲染,至少在js隊列不空的情況下,根本沒有機會渲染的,那麽就可以做一個代碼不斷的使用setInternal不斷的往隊列裏面填消息。事實真的是這樣嗎?

肯定不是這樣的,實際的瀏覽器沒有是這樣的,那麽到底是哪塊讓js引擎和gui執行引擎的線程進行切換的,同步代碼肯定不行,那麽就是隊列這塊了,肯定不是隊列為空的情況切換的,因為實際的js代碼在改了之後基本上就渲染了,那麽基本上確定,是在每一次消息之間執行的,因為js代碼在執行的時候是同步的,做了這個假設之後,再查了大量的資料,有一篇文章是這樣描述的,在每次執行完一個消息之後,馬上切換到渲染線程執行渲染效果,然後渲染完成再切換js代碼執行取下一個消息。

至此渲染和js代碼執行的交互就了解了。(渲染線程是每次都需要切換的嗎?這個已經屬於性能優化內容了,就暫時不了解了)

試著講清楚:js代碼運行機制