摘要:在這個視頻中,將的調(diào)用棧回調(diào)隊列和事件循環(huán)的內(nèi)容講的很清晰。調(diào)用棧可以往里面放東西,可以在事件結(jié)束的時候把回調(diào)函數(shù)放進(jìn)回調(diào)隊列,然后是事件循環(huán)。為的時候這個過程看起來可能不明顯,除非考慮到調(diào)用棧的執(zhí)行環(huán)境和事件循環(huán)的情況。
譯者按
這篇文章可以看做是對Philip Roberts 2014年在JSConf演講的《What the heck is the event loop anyway?》的一個總結(jié)。
建議先看Philip Roberts的這個演講然后再閱讀本篇文章。這哥們兒的演講語言幽默風(fēng)趣,內(nèi)容通俗易懂,非常值得一看。
在這個視頻中,Philip Roberts將JavaScript的調(diào)用棧、回調(diào)隊列和事件循環(huán)的內(nèi)容講的很清晰。所以你可以隨意的跳過這篇文章,花上一個半小時去看視頻。當(dāng)然如果你愿意讀一下我的這篇文章那也不是不可以。
什么是JavaScript什么是JavaScript呢?列舉一些關(guān)鍵詞就是:
他是單線程的、非阻塞的、異步的并發(fā)語言
他有一個調(diào)用棧,一個事件循環(huán),一個回調(diào)隊列,還有一些api和別的東西
如果你像我一樣(或者像Philip Roberts)對此懵逼的話,這些話本身并沒不意味著什么。那我們就來剖析一下。
JavaScript運行時JavaScript運行時(像V8引擎)擁有一個堆(內(nèi)存分配用的)和棧(執(zhí)行上下文)。但是他沒有setTimeout、DOM等。這些是瀏覽器提供的Web APIs。
我們了解的JavaScript瀏覽器中的JavaScript擁有:
一個像V8引擎一樣的運行時(提供堆棧)
瀏覽器提供的Web APIs,例如:DOM、AJAX和setTimeout
一個為各種事件回調(diào)準(zhǔn)備的回調(diào)隊列,例如:onClick、onLoad、onDone
一個事件循環(huán)
什么是調(diào)用棧JavaScript是單線程的,意味著他有一個多帶帶的調(diào)用棧,意味著他一次能做一件事。調(diào)用棧基本上就是一個記錄程序執(zhí)行位置的數(shù)據(jù)結(jié)構(gòu)。如果程序進(jìn)入了一個函數(shù),那就往這個棧里面塞些東西。如果程序從一個函數(shù)中return了,那就從棧頂彈出一些東西。
當(dāng)我們的程序報錯的時候,我們會在控制臺看到調(diào)用棧信息。報錯的時候我們可以看到棧的狀態(tài)(被調(diào)用的那個函數(shù)的)。
阻塞這涉及到一個重要的問題:程序運行的很慢的時候發(fā)生了什么?換句話說,就是程序阻塞了。阻塞并沒有嚴(yán)格的定義。實際上就是程序執(zhí)行慢。執(zhí)行console.log不慢,但是一個從1到1,000,000,000的while循環(huán),圖像處理或者網(wǎng)絡(luò)請求這些操作的執(zhí)行就比較費時了。這些執(zhí)行慢的東西堆在一起就發(fā)生了阻塞。
因為JavaScript是單線程的,我們發(fā)起一個網(wǎng)絡(luò)請求就不得不一直等到他結(jié)束。這在瀏覽器中就是個問題--當(dāng)我們等這個請求的時候,瀏覽器就發(fā)生了阻塞(我們不能做點擊、提交表單等操作)。解決這個問題的方法就是使用異步回調(diào)。
并發(fā),看到這個詞的時候我們會發(fā)現(xiàn)上面有一個地方說的不對JavaScript一次只能做一件事情的說法是不對的。正確的說法應(yīng)該是:JavaScript的運行時一次只能做一件事。他不能一邊發(fā)ajax請求一邊運行別的代碼,也不能在執(zhí)行別的代碼時候運行一個定時器。但是我們可以并發(fā)的做這些事。因為瀏覽器不僅僅是一個運行時(還記得上面那個渣渣畫質(zhì)的圖嗎?)。
調(diào)用??梢酝鵚eb APIs里面放東西,Web APIs可以在事件結(jié)束的時候把回調(diào)函數(shù)放進(jìn)回調(diào)隊列,然后是事件循環(huán)。最終我們進(jìn)入事件循環(huán),這是這個過程中最簡單的部分,他有一個非常簡單的工作:看看調(diào)用棧,瞅瞅回調(diào)隊列,如果調(diào)用??臻e了,就把回調(diào)隊列中的第一個函數(shù)取出來丟進(jìn)調(diào)用棧讓他執(zhí)行(這就回到了JavaScript的地盤,回到了V8的內(nèi)部)。
整個串起來Philip搞了一個的碉堡的工具來可視化這個過程,這玩意兒叫Loupe。這是一個能夠把JavaScript運行時可視化的工具。
我們用它來看一個簡單的例子:在一個異步的setTimeout回調(diào)中用console.log在控制臺打些log出來。
整個過程到底都發(fā)生了什么呢?我們來看一下:
執(zhí)行進(jìn)入console.log("Hi");函數(shù),因此這個函數(shù)被丟進(jìn)了調(diào)用棧里。
console.log("Hi");函數(shù)return了,因此他就被彈出了棧頂。
執(zhí)行進(jìn)入setTimeout函數(shù),因此這個函數(shù)被丟進(jìn)了調(diào)用棧里。
setTimeout是Web APIs的一部分,因此Web APIs處理了他,并且等了2秒
繼續(xù)執(zhí)行腳本,進(jìn)入console.log("EvenyBody")函數(shù),把他也丟進(jìn)調(diào)用棧。
console.log("EvenyBody")函數(shù)return了,所以把他從棧頂彈出去
2秒的定時已經(jīng)完成了,所以就把對應(yīng)的回調(diào)函數(shù)放到回調(diào)隊列里。
事件循環(huán)檢查調(diào)用棧是否為空,如果非空的話,他就等著。因為調(diào)用棧現(xiàn)在是空的,所以把回調(diào)隊列中的回調(diào)函數(shù)丟進(jìn)調(diào)用棧。
console.log("There")函數(shù)返回了,因此把他從棧頂彈出去(譯者按:原文為console.log("Everybody"),應(yīng)為書寫錯誤)。
有趣的一點是:setTimeout(function(...), 0)的情況。setTimeout為0的時候這個過程看起來可能不明顯,除非考慮到調(diào)用棧的執(zhí)行環(huán)境和事件循環(huán)的情況。基本上都會推遲到調(diào)用棧為空才執(zhí)行。
考慮UI渲染的性能的情況為了回到了我們?nèi)粘L幚淼腢I層,我們需要考慮渲染問題。瀏覽器受到我們在JavaScript中所做操作的影響,他可能每隔16.6ms重繪一次屏幕(60幀/秒)。但是調(diào)用棧還有代碼在執(zhí)行的話,他實際上是沒法做重繪的。
就像Philip說的一樣:
當(dāng)大家說不要"阻塞事件循環(huán)"的時候,他們實際上是說:不要把耗費時間長的代碼放進(jìn)調(diào)用棧,因為你要這么搞的話,瀏覽器就不能做他該做的事了,比如說給你搞一個漂亮流暢的UI。Philip Roberts “What the Heck Is the Event Loop Anyway”
舉個例子,滾動的處理函數(shù)觸發(fā)多了會讓UI變得卡頓。順便說一句,這是我聽過的對防抖最清楚的解釋了,這就是你要做到的“不要阻塞事件循環(huán)”(那就是我們只在滾動處理函數(shù)被觸發(fā)x次后才執(zhí)行那些耗時的操作)。
結(jié)語總之,這就是《What the heck is the event loop anyway?》的答案。Philip的演講很好的幫我理解了什么是JavaScript,什么不是,哪個部分是運行時,哪個部分是瀏覽器的和我們該怎樣有效的使用事件循環(huán)。好好看看這個視頻吧。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/101789.html
摘要:問題什么是調(diào)用棧并且它是的一部分么調(diào)用棧當(dāng)然是的一部分。為什么理解是重要的因為你在每個進(jìn)程中只能獲取一個調(diào)用棧。它是一個從事件隊列中跳去事件的循環(huán)并且將它們的回調(diào)壓入到調(diào)用棧中。當(dāng)調(diào)用棧為空的時候,事件循環(huán)可以決定下一步執(zhí)行哪一個。 你并不知道Node 原文:You don’t know Node 譯者:neal1991 welcome to star my articles-tra...
摘要:本章會對語言引擎,運行時,調(diào)用棧做一個概述。調(diào)用棧只是一個單線程的編程語言,這意味著它只有一個調(diào)用棧。查看如下代碼當(dāng)引擎開始執(zhí)行這段代碼的時候,調(diào)用棧會被清空。之后,產(chǎn)生如下步驟調(diào)用棧中的每個入口被稱為堆棧結(jié)構(gòu)。 原文請查閱這里,本文采用知識共享署名 4.0 國際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原...
摘要:本章會對語言引擎,運行時,調(diào)用棧做一個概述。調(diào)用棧只是一個單線程的編程語言,這意味著它只有一個調(diào)用棧。查看如下代碼當(dāng)引擎開始執(zhí)行這段代碼的時候,調(diào)用棧會被清空。之后,產(chǎn)生如下步驟調(diào)用棧中的每個入口被稱為堆棧結(jié)構(gòu)。 原文請查閱這里,本文采用知識共享署名 4.0 國際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原...
摘要:調(diào)用棧是一種數(shù)據(jù)結(jié)構(gòu),它記錄了我們在程序中的位置。當(dāng)從這個函數(shù)返回的時候,就會將這個函數(shù)從棧頂彈出,這就是調(diào)用棧做的事情。而且這不是唯一的問題,一旦你的瀏覽器開始處理調(diào)用棧中的眾多任務(wù),它可能會停止響應(yīng)相當(dāng)長一段時間。 原文地址: https://blog.sessionstack.com... PS: 好久沒寫東西了,最近一直在準(zhǔn)備寫一個自己的博客,最后一些技術(shù)方向已經(jīng)敲定了,又可以...
摘要:當(dāng)函數(shù)結(jié)束,將會被從調(diào)用棧移出。事件循環(huán)事件循環(huán)的責(zé)任就是查看調(diào)用棧并確定調(diào)用棧是否為空。事件循環(huán)會再次檢查調(diào)用棧是否為空,如果為空的話,它會把事件回調(diào)壓入棧中,然后回調(diào)函數(shù)則被執(zhí)行。 寫在文章前 這篇文章是翻譯自Sukhjinder Arora的Understanding Asynchronous JavaScript。這篇文章描述了異步和同步JavaScript是如何在運行環(huán)境中,...
閱讀 1156·2021-11-08 13:13
閱讀 1733·2019-08-30 15:55
閱讀 2791·2019-08-29 11:26
閱讀 2456·2019-08-26 13:56
閱讀 2583·2019-08-26 12:15
閱讀 2156·2019-08-26 11:41
閱讀 1421·2019-08-26 11:00
閱讀 1555·2019-08-23 18:30