前言
通過生動形象的動圖講解事件循環的一些基本概念。今日早讀文章由@Logan70翻譯授權分享。
正文從這開始~~
事件循環(Event Loop),是每個JS開發者都會接觸到的概念,但是剛接觸時可能會存在各種疑惑。我是一個視覺型學習者,所以打算通過gif動圖的可視化形式幫助大家理解它。
首先我們來看看,什麼是事件循環,我們為什麼要了解它呢?
眾所周知,JavaScript是 單線程(single-threaded) 的,也就是同一時間只能運行一個任務。一般情況下這並沒有什麼問題,但是假如我們要運行一個耗時30秒的任務,我們就得等待30秒後才能執行下一個任務(這30秒期間,JavaScript佔用了主線程,我們什麼都不能做,包括頁面也是卡死狀態)。這都9012年了,不帶這麼坑爹的吧?
好在瀏覽器向我們提供了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之後,回調竟然沒有放入調用棧執行,而是被放入了任務隊列,那什麼時候被執行呢?不要急,既然是一個隊列,那就要排排坐,吃果果。
接下來就是我們期待已久,萬眾矚目的 事件循環(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執行完畢出棧;
500ms後定時器運行完畢,回調函數被放入任務隊列;
Event Loop檢測到調用棧為空,從任務隊列中取出回調函數放入調用棧;
回調函數被執行,列印Second,執行完畢出棧。
關於本文譯者:@Logan70譯文:https://github.com/logan70/Blog/issues/25作者:@Lydia Hallie原文:https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif
為你推薦
【第1405期】瀏覽器的 Event Loop
【第993期】總是一知半解的Event Loop
【第1431期】圖解瀏覽器的基本工作原理