譯者按
這篇文章能夠看作是對Philip Roberts 2014年在JSConf演講的 《What the heck is the event loop anyway?》的一個總結。
建議先看Philip Roberts的這個演講而後再閱讀本篇文章。這哥們兒的演講語言幽默風趣,內容通俗易懂,很是值得一看。
在這個視頻中,Philip Roberts將JavaScript的調用棧、回調隊列和事件循環的內容講的很清晰。因此你能夠隨意的跳過這篇文章,花上一個半小時去看視頻。固然若是你願意讀一下個人這篇文章那也不是不能夠。ajax
什麼是JavaScript呢?列舉一些關鍵詞就是:api
若是你像我同樣(或者像Philip Roberts)對此懵逼的話,這些話自己並沒不意味着什麼。那咱們就來剖析一下。瀏覽器
JavaScript運行時(像V8引擎)擁有一個堆(內存分配用的)和棧(執行上下文)。可是他沒有setTimeout
、DOM
等。這些是瀏覽器提供的Web APIs
。網絡
瀏覽器中的JavaScript擁有:數據結構
DOM
、AJAX
和setTimeout
onClick
、onLoad
、onDone
JavaScript是單線程的,意味着他有一個單獨的調用棧,意味着他一次能作一件事。調用棧基本上就是一個記錄程序執行位置的數據結構。若是程序進入了一個函數,那就往這個棧裏面塞些東西。若是程序從一個函數中return了,那就從棧頂彈出一些東西。併發
當咱們的程序報錯的時候,咱們會在控制檯看到調用棧信息。報錯的時候咱們能夠看到棧的狀態(被調用的那個函數的)。異步
這涉及到一個重要的問題:程序運行的很慢的時候發生了什麼?換句話說,就是程序阻塞了。阻塞並無嚴格的定義。實際上就是程序執行慢。執行console.log
不慢,可是一個從1到1,000,000,000的while循環,圖像處理或者網絡請求這些操做的執行就比較費時了。這些執行慢的東西堆在一塊兒就發生了阻塞。函數
由於JavaScript是單線程的,咱們發起一個網絡請求就不得不一直等到他結束。這在瀏覽器中就是個問題--當咱們等這個請求的時候,瀏覽器就發生了阻塞(咱們不能作點擊、提交表單等操做)。解決這個問題的方法就是使用異步回調。工具
JavaScript一次只能作一件事情的說法是不對的。正確的說法應該是:JavaScript的運行時一次只能作一件事。他不能一邊發ajax請求一邊運行別的代碼,也不能在執行別的代碼時候運行一個定時器。可是咱們能夠併發的作這些事。由於瀏覽器不單單是一個運行時(還記得上面那個渣渣畫質的圖嗎?)。oop
調用棧能夠往Web APIs裏面放東西,Web APIs能夠在事件結束的時候把回調函數放進回調隊列,而後是事件循環。最終咱們進入事件循環,這是這個過程當中最簡單的部分,他有一個很是簡單的工做:看看調用棧,瞅瞅回調隊列,若是調用棧空閒了,就把回調隊列中的第一個函數取出來丟進調用棧讓他執行(這就回到了JavaScript的地盤,回到了V8的內部)。
Philip搞了一個的碉堡的工具來可視化這個過程,這玩意兒叫Loupe。這是一個可以把JavaScript運行時可視化的工具。
咱們用它來看一個簡單的例子:在一個異步的setTimeout
回調中用console.log
在控制檯打些log出來。
整個過程到底都發生了什麼呢?咱們來看一下:
console.log('Hi');
函數,所以這個函數被丟進了調用棧裏。console.log('Hi');
函數return了,所以他就被彈出了棧頂。setTimeout
函數,所以這個函數被丟進了調用棧裏。setTimeout
是Web APIs
的一部分,所以Web APIs
處理了他,而且等了2秒console.log('EvenyBody')
函數,把他也丟進調用棧。console.log('EvenyBody')
函數return了,因此把他從棧頂彈出去console.log('There')
函數返回了,所以把他從棧頂彈出去(譯者按:原文爲console.log('Everybody'),應爲書寫錯誤)。有趣的一點是:setTimeout(function(...), 0)
的狀況。setTimeout
爲0的時候這個過程看起來可能不明顯,除非考慮到調用棧的執行環境和事件循環的狀況。基本上都會推遲到調用棧爲空才執行。
爲了回到了咱們平常處理的UI層,咱們須要考慮渲染問題。瀏覽器受到咱們在JavaScript中所作操做的影響,他可能每隔16.6ms重繪一次屏幕(60幀/秒)。可是調用棧還有代碼在執行的話,他其實是無法作重繪的。
就像Philip說的同樣:
當你們說不要"阻塞事件循環"的時候,他們其實是說:不要把耗費時間長的代碼放進調用棧,由於你要這麼搞的話,瀏覽器就不能作他該作的事了,好比說給你搞一個漂亮流暢的UI。Philip Roberts 「What the Heck Is the Event Loop Anyway」
舉個例子,滾動的處理函數觸發多了會讓UI變得卡頓。順便說一句,這是我聽過的對防抖最清楚的解釋了,這就是你要作到的「不要阻塞事件循環」(那就是咱們只在滾動處理函數被觸發x次後才執行那些耗時的操做)。
總之,這就是《What the heck is the event loop anyway?》的答案。Philip的演講很好的幫我理解了什麼是JavaScript,什麼不是,哪一個部分是運行時,哪一個部分是瀏覽器的和咱們該怎樣有效的使用事件循環。好好看看這個視頻吧。