對於怎麼理解js中Event Loop,你能夠看這篇文章

javascript中event loop是什麼

聲明

源文檔地址javascript

介紹

如何你跟我同樣的話,那麼你必定會愛上javascript!雖然它不是一種比較完美的編程語言,可是嚴格地說,還有其它比javascript更完美的語言來設計web嗎?因此請忽視javascript的缺陷吧。我喜歡web工做,而javascript使得我能夠用其來創建applications,從而跟web上的用戶聯繫起來!java

可是若是你深究javascript——你會發現,它的一些內部概念你須要花費許多時間才能真正理解!其中一個概念就是Event Loop,可能一個在web開發中工做了幾年的開發人員也沒有真正理解其含義和工做原理,無論怎樣,我但願經過這篇文章,能帶給你一些啓發:什麼是event loop?其實它並非那麼難以理解的!node

瀏覽器中的Javascript

咱們一般所說的javascript,都是指瀏覽器端使用的javascript——由於咱們寫的javascript代碼大多數是用在客戶端的!無論怎樣,理解這些概念和技術是很是重要的:一、Javascript Engine(好比 Chrome's V8),二、Web API(如 DOM),三、Event Loop 和 Event Quene。git

你可能看過上面列舉的這些概念以後,你可能會這樣想:「好複雜呀!」,確實是這樣的!可是經過這篇文章待會兒你會發現,其實它們並無那麼複雜!github

在介紹event loop以前,咱們須要對javascript engine作一個簡單的理解來明白它是作什麼的!web

Javascript Engine

有幾種不一樣的js引擎,可是最流行的一個當屬谷歌的V8了(它並不只僅在瀏覽器環境中使用,經過nodejs,它也在服務器端使用)。可是js引擎的工做究竟是什麼呢?其實,引擎的工做很是簡單——它會遍歷咱們寫的全部js代碼,而後一個一個的執行它們。也就是說咱們的js是單線程的,單線程很差的地方就是,若是你同步執行了一段很長時間的代碼的話,那麼這段代碼後面的邏輯會被阻塞從而等待好久才能執行。同門一般都不想寫阻塞的代碼——特別是在瀏覽器中,設想若是你點擊了一個按鈕以後去執行了一個很長時間的阻塞代碼,這會致使你的頁面的其它交互效果會暫停,嚴重影響用戶體驗!編程

那麼js引擎是怎麼知道一個一個的處理代碼呢?其實它使用了call stack(調用棧)。你能夠把調用棧看做一個電梯——第一個進電梯的人最後一個出來,最後一個進電梯的人第一個出來!瀏覽器

咱們來看一個例子:服務器

/*Within main.js*/
 var firstFunction = function(){
     console.log("I'm first!");
 };
 var secondFunction = function(){
     firstFunction();
     console.log("I'm second!");
 };
 secondFunction();
 /*Results: *=> I'm first! *=> I'm second! */複製代碼

首先,js引擎會維護一個調用棧序列的:app

  • main.js第一次執行的時候,調用棧序列是這樣的:

  • 當調用secondFunction的時候,調用棧序列:

  • 咱們調用secondFunction致使firstFunction被調用,因此此時序列是這樣的:

  • 當執行完firstFunction,會打印「I'm first!」,而後在其內部就沒有其它代碼執行了,因此調用棧序列須要把firstFunction移除,因此此時序列是這樣的:

  • 一樣,secondFunction打印完「I'm second!」以後,在其內部也沒有其它代碼須要執行了,因此會移除其,因此此時序列是這樣的:

  • 最後,當main.js中沒有其它代碼須要執行的時候,那麼匿名函數也會被從調用棧中移除,從而這樣:

好了,說了半天,什麼是Event Loop?

到如今,咱們已經知道js引擎的調用棧的原理了,那麼咱們回到剛纔所討論的阻塞代碼的問題上,咱們都知道應該避免寫阻塞代碼!慶幸的是,js提供了一種機制來實現非阻塞:異步回調函數!咱們又接觸了一個概念,是否是覺得這個概念很牛逼呢?其實並非,異步回調函數跟你寫過的其它的普通的函數同樣的!只不過它不是同步執行的,若是你熟悉的setTimeout函數的話,它就是異步的,咱們來看一個例子:

/* Within main.js */

var firstFunction = function () {  
 console.log("I'm first!");
};

var secondFunction = function () {  
 setTimeout(firstFunction, 5000);
 console.log("I'm second!");
};

secondFunction();

/* Results: * => I'm second! * (And 5 seconds later) * => I'm first! */複製代碼

咱們看一下調用棧序列是怎麼樣的!(爲了儘快說明問題,咱們快進了)

  • 當調用secondFunction的時候,調用setTimeout函數,因此序列是這樣的:

  • 當調用棧在調用setTimeout的時候,發生了一塊兒特別的事情——瀏覽器會把setTimeout的回調函數放到(在這個例子中就是firstFunction)Event Table中。你能夠把Event Table理解成爲一個註冊機構,調用棧會告訴Event Table去註冊一個特別的函數,當特定的事件發生的時候去執行它,當特定的事件發生時,Event Table只是簡單的把這個函數移動到Event Quene裏面,Event Quene只是一個放置函數即將執行的暫存區,最後由Event Quene把函數移動到調用棧中。

你可能會問,Event Quene何時知道把回調函數放回到調用棧中呢?好,js引擎聽從一個簡單的規則:瀏覽器中存在一個進程會不斷地去檢查調用棧是否爲空,同時,不管任什麼時候候只要調用棧爲空,它會檢查Event Quene是否有待執行的函數隊列!若是調用棧爲空,那麼它會把Event Quene隊列裏面的第一個函數放到調用棧中,若是Event Quene爲空的話,那麼這個進程將會無限期地來回地進行檢查,好吧——咱們描述的這個就是所謂的Event Loop!

  • 如今回到咱們的例子,執行的setTimeout函數的時候,咱們向Event Table裏面註冊了一個回調函數(在這個例子中是firstFunction),而且延遲5秒可執行!調用棧序列此時是這樣的:

  • 咱們運行代碼的時候會發現,咱們的代碼並無等待5秒以後纔打印「I'm second!」,而是天然地執行了下一行代碼,此時代碼序列是這樣的:

  • 在背後,Event Table會不斷的監視跟回調函數相關的時候那個事件(這個例子中爲等待5秒)是否發生,從而把回調函數移動到可執行的Event Quene序列裏面。與此同時,secondFunction和main.js中匿名函數都執行完了,因此調用棧序列此時是這樣的:

  • 通過5秒以後,event table會移動firstFunction到event quene裏面:

  • 同時event loop不斷的監視當前調用棧是否爲空,若是是,則把event quene序列裏面的第一個回調函數放到調用棧(新的,不一樣於剛纔的調用棧,剛纔的已經沒有了)裏面。因此此時調用棧序列是這樣的:

  • 當firstFunction執行完成以後,瀏覽器會返回一個狀態:調用棧會空,event table沒有任何事件可監聽,同時Event Quene隊列爲空!最後是這樣的:

相關文章
相關標籤/搜索