摘要:在這個視頻中,將的調用棧回調隊列和事件循環的內容講的很清晰。調用棧可以往里面放東西,可以在事件結束的時候把回調函數放進回調隊列,然后是事件循環。為的時候這個過程看起來可能不明顯,除非考慮到調用棧的執行環境和事件循環的情況。
譯者按
這篇文章可以看做是對Philip Roberts 2014年在JSConf演講的《What the heck is the event loop anyway?》的一個總結。
建議先看Philip Roberts的這個演講然后再閱讀本篇文章。這哥們兒的演講語言幽默風趣,內容通俗易懂,非常值得一看。
在這個視頻中,Philip Roberts將JavaScript的調用棧、回調隊列和事件循環的內容講的很清晰。所以你可以隨意的跳過這篇文章,花上一個半小時去看視頻。當然如果你愿意讀一下我的這篇文章那也不是不可以。
什么是JavaScript什么是JavaScript呢?列舉一些關鍵詞就是:
他是單線程的、非阻塞的、異步的并發語言
他有一個調用棧,一個事件循環,一個回調隊列,還有一些api和別的東西
如果你像我一樣(或者像Philip Roberts)對此懵逼的話,這些話本身并沒不意味著什么。那我們就來剖析一下。
JavaScript運行時JavaScript運行時(像V8引擎)擁有一個堆(內存分配用的)和棧(執行上下文)。但是他沒有setTimeout、DOM等。這些是瀏覽器提供的Web APIs。
我們了解的JavaScript瀏覽器中的JavaScript擁有:
一個像V8引擎一樣的運行時(提供堆棧)
瀏覽器提供的Web APIs,例如:DOM、AJAX和setTimeout
一個為各種事件回調準備的回調隊列,例如:onClick、onLoad、onDone
一個事件循環
什么是調用棧JavaScript是單線程的,意味著他有一個多帶帶的調用棧,意味著他一次能做一件事。調用棧基本上就是一個記錄程序執行位置的數據結構。如果程序進入了一個函數,那就往這個棧里面塞些東西。如果程序從一個函數中return了,那就從棧頂彈出一些東西。
當我們的程序報錯的時候,我們會在控制臺看到調用棧信息。報錯的時候我們可以看到棧的狀態(被調用的那個函數的)。
阻塞這涉及到一個重要的問題:程序運行的很慢的時候發生了什么?換句話說,就是程序阻塞了。阻塞并沒有嚴格的定義。實際上就是程序執行慢。執行console.log不慢,但是一個從1到1,000,000,000的while循環,圖像處理或者網絡請求這些操作的執行就比較費時了。這些執行慢的東西堆在一起就發生了阻塞。
因為JavaScript是單線程的,我們發起一個網絡請求就不得不一直等到他結束。這在瀏覽器中就是個問題--當我們等這個請求的時候,瀏覽器就發生了阻塞(我們不能做點擊、提交表單等操作)。解決這個問題的方法就是使用異步回調。
并發,看到這個詞的時候我們會發現上面有一個地方說的不對JavaScript一次只能做一件事情的說法是不對的。正確的說法應該是:JavaScript的運行時一次只能做一件事。他不能一邊發ajax請求一邊運行別的代碼,也不能在執行別的代碼時候運行一個定時器。但是我們可以并發的做這些事。因為瀏覽器不僅僅是一個運行時(還記得上面那個渣渣畫質的圖嗎?)。
調用棧可以往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了,所以把他從棧頂彈出去
2秒的定時已經完成了,所以就把對應的回調函數放到回調隊列里。
事件循環檢查調用棧是否為空,如果非空的話,他就等著。因為調用棧現在是空的,所以把回調隊列中的回調函數丟進調用棧。
console.log("There")函數返回了,因此把他從棧頂彈出去(譯者按:原文為console.log("Everybody"),應為書寫錯誤)。
有趣的一點是:setTimeout(function(...), 0)的情況。setTimeout為0的時候這個過程看起來可能不明顯,除非考慮到調用棧的執行環境和事件循環的情況。基本上都會推遲到調用棧為空才執行。
考慮UI渲染的性能的情況為了回到了我們日常處理的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,什么不是,哪個部分是運行時,哪個部分是瀏覽器的和我們該怎樣有效的使用事件循環。好好看看這個視頻吧。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101789.html
摘要:問題什么是調用棧并且它是的一部分么調用棧當然是的一部分。為什么理解是重要的因為你在每個進程中只能獲取一個調用棧。它是一個從事件隊列中跳去事件的循環并且將它們的回調壓入到調用棧中。當調用棧為空的時候,事件循環可以決定下一步執行哪一個。 你并不知道Node 原文:You don’t know Node 譯者:neal1991 welcome to star my articles-tra...
摘要:本章會對語言引擎,運行時,調用棧做一個概述。調用棧只是一個單線程的編程語言,這意味著它只有一個調用棧。查看如下代碼當引擎開始執行這段代碼的時候,調用棧會被清空。之后,產生如下步驟調用棧中的每個入口被稱為堆棧結構。 原文請查閱這里,本文采用知識共享署名 4.0 國際許可協議共享,BY Troland。 本系列持續更新中,Github 地址請查閱這里。 這是 JavaScript 工作原...
摘要:本章會對語言引擎,運行時,調用棧做一個概述。調用棧只是一個單線程的編程語言,這意味著它只有一個調用棧。查看如下代碼當引擎開始執行這段代碼的時候,調用棧會被清空。之后,產生如下步驟調用棧中的每個入口被稱為堆棧結構。 原文請查閱這里,本文采用知識共享署名 4.0 國際許可協議共享,BY Troland。 本系列持續更新中,Github 地址請查閱這里。 這是 JavaScript 工作原...
摘要:調用棧是一種數據結構,它記錄了我們在程序中的位置。當從這個函數返回的時候,就會將這個函數從棧頂彈出,這就是調用棧做的事情。而且這不是唯一的問題,一旦你的瀏覽器開始處理調用棧中的眾多任務,它可能會停止響應相當長一段時間。 原文地址: https://blog.sessionstack.com... PS: 好久沒寫東西了,最近一直在準備寫一個自己的博客,最后一些技術方向已經敲定了,又可以...
摘要:當函數結束,將會被從調用棧移出。事件循環事件循環的責任就是查看調用棧并確定調用棧是否為空。事件循環會再次檢查調用棧是否為空,如果為空的話,它會把事件回調壓入棧中,然后回調函數則被執行。 寫在文章前 這篇文章是翻譯自Sukhjinder Arora的Understanding Asynchronous JavaScript。這篇文章描述了異步和同步JavaScript是如何在運行環境中,...
閱讀 1123·2021-11-08 13:13
閱讀 1707·2019-08-30 15:55
閱讀 2762·2019-08-29 11:26
閱讀 2427·2019-08-26 13:56
閱讀 2549·2019-08-26 12:15
閱讀 2125·2019-08-26 11:41
閱讀 1394·2019-08-26 11:00
閱讀 1530·2019-08-23 18:30