原文: 《JavaScript Visualized: Event Loop》 - By Lydia Hallie
本文主要經過生動形象的動圖講解事件循環的一些基本概念,主要面向初學者,譯者已取得原做者的贊成。本文一些部分採用意譯,以幫助你們更好地理解。
本文首發於我的博客 Logan's Blog,其餘JS相關內容也可前往小弟博客共同窗習探討。javascript
事件循環(Event Loop),是每一個JS開發者都會接觸到的概念,可是剛接觸時可能會存在各類疑惑。我是一個視覺型學習者,因此打算經過gif動圖的可視化形式幫助你們理解它。java
首先咱們來看看,什麼是事件循環,咱們爲何要了解它呢?git
衆所周知,JavaScript是 單線程(single-threaded) 的,也就是同一時間只能運行一個任務。通常狀況下這並無什麼問題,可是假如咱們要運行一個耗時30秒的任務,咱們就得等待30秒後才能執行下一個任務(這30秒期間,JavaScript佔用了主線程,咱們什麼都不能作,包括頁面也是卡死狀態)。這都9012年了,不帶這麼坑爹的吧?github
好在瀏覽器向咱們提供了JS引擎不具有的特性:Web API
。Web API
包括DOM API
、定時器
、HTTP請求
等特性,能夠幫助咱們實現異步、非阻塞的行爲。瀏覽器
當咱們調用一個函數時,函數會被放入一個叫作調用棧(call stack,也叫執行上下文棧)的地方。調用棧是JS引擎的一部分,並不是瀏覽器特有的。調用棧是一個棧數據結構,具備後進先出的特色(Last in, first out. LIFO)。當函數執行完畢返回時,會被彈出調用棧。數據結構
圖例中的respond
函數返回一個setTimeout
函數調用,setTimeout
函數是Web API
提供給咱們的功能:它容許咱們延遲執行一個任務而不用阻塞主線程。setTimeout
被調用時,咱們傳入的回調函數,即箭頭函數() => { return 'hey' }
會被傳遞給Web API
處理,而後setTimeout
和respond
依次執行完畢出棧。異步
在Web API
中會執行定時器,定時間隔就是咱們傳入setTimeout
的第二個參數,也就是1000ms。計時結束後回調函數並不會當即進入調用棧執行,而是會被加入一個叫作 任務隊列(Task Queue) 的地方。函數
看到這裏,有些人可能會疑惑:1000ms以後,回調居然沒有放入調用棧執行,而是被放入了任務隊列,那何時被執行呢?不要急,既然是一個隊列,那就要排排坐,吃果果。oop
接下來就是咱們期待已久,萬衆矚目的 事件循環(Event Loop) 閃亮登場的時刻了。Event Loop的工做就是鏈接任務隊列和調用棧,當調用棧中的任務均執行完畢出棧,調用棧爲空時,Event Loop會檢查任務隊列中是否存在等待執行的任務,若是存在,則取出隊列中第一個任務,放入調用棧。學習
咱們的回調函數被放入調用棧中,執行完畢,返回其返回值,而後被彈出調用棧。
閱讀一時爽,但只有經過反覆練習,將其變爲本身的東西后纔會一直爽。咱們來作個小練習檢測下學習成果,看看下面代碼輸出什麼:
const foo = () => console.log('First');
const bar = () => setTimeout(() => console.log('Second'), 500);
const baz = () => console.log('Third');
bar();
foo();
baz();
複製代碼
相信你們均可以輕鬆給出正確答案。咱們一塊兒來看下這段代碼運行時發生了什麼:
bar
被調用,返回setTimeout
的調用;setTimeout
的回調被傳遞給Web API
處理,setTimeout
執行完畢出棧,bar
執行完畢出棧;foo
被調用,打印First
,foo
執行完畢出棧;baz
被調用,打印Third
,baz
執行完畢出棧;Second
,執行完畢出棧。但願本文能幫助你對事件循環有一個初步的瞭解,若是還有疑惑,可留言交流探討。