js事件循環機制辨析

 對於新接觸js語言的人來講,最使人困惑的大概就是事件循環機制了。最開始這也困惑了我很久,花了我幾個月時間經過書本,打代碼,查閱資料不停地漸進地理解他。接下來我想要和你們分享一下,雖然可能有些許錯誤的地方,但願你們不吝賜教,感謝感謝。html

 這是所涉及的知識點:html5

  • 觀察者模式
  • js的事件循環機制
    • js事件循環機制優缺點及與多線程的比較

觀察者模式

 js的事件循環機制是基於觀察者模式的,而跟觀察者模式相對應的是輪詢,咱們先來講說輪詢的原理。node

 咱們將輪詢映射在現實世界中即爲:B不停到A的房間觀察房間裏是否有人,從而知道A是否回來。web

 但顯然,這是效率極低的,咱們回到代碼層面上。B線程使用while(true){觀察A的房間,當A在房間內時退出循環}來作到輪詢。可是,這樣B線程就被堵塞住了,除非退出該循環,不然沒法執行接下來的同步代碼及異步代碼。這對於單線程語言是徹底沒法接受的,因此咱們來看看觀察者模式,他是否會堵塞線程。瀏覽器

 一樣的,咱們來將觀察者模式映射到現實世界中:B在本身房間作本身的事情,再也不不停地到A的房間看他是否回來,而是當A回到本身房間時,打電話通知B他回來了,B再去房間找A玩。多線程

 該模式最大的優點就是:B能夠在等待A回房間的期間,作本身的事情。回到代碼層面上,使用觀察者模式後,B線程再也不被堵塞,A回到房間的信息再也不須要B經過循環來同步地監聽,而是A用消息傳給B線程,B再根據這個消息來執行當A回到房間後應該執行的操做。併發

 其實當理解了觀察者模式的大致流程就已經可以理解js的事件循環機制了。但瞭解得深刻些也沒有壞處。接下來咱們來用js代碼來模擬出一個簡易的觀察者模式。
代碼以下:oracle

var b = {
process_a:mes=>{
    console.log('剛剛A發了 %s 的信息,因此我知道A回來了,我該去他房間找他玩了。',mes)
}
}

function A(b){
    var mes_a = '我是A,我回來了'
    b.process_a(mes_a)
}

A(b)

 結果以下:
異步

若是你們對同步,異步,堵塞,非堵塞的概念有不理解的地方的話,能夠看個人 同步,異步,堵塞,非堵塞,併發 辨析。函數


事件循環機制

 事件循環機制的核心就是觀察者模式。我先給你們描述一遍程序執行的流程。

  1. js程序進入線程,函數入棧,當遇到同步代碼的時候就順序執行,遇到異步代碼時,把異步任務拋給WebAPIs執行,而後繼續執行接下來的同步代碼,直到棧爲空。(如若你們對函數棧不瞭解的話能夠看下個人 棧,堆辨析及使用)
  2. 在步驟1進行的同時,WebAPIs執行異步任務,當執行完一個異步任務就將其對應的回調函數放入任務隊列(Callback Queue)中等待。
    • WebAPIs是由C++實現的瀏覽器建立的線程,處理諸如DOM事件,http請求,定時器等異步任務。
  3. 當執行棧爲空時,從Callback Queue中取出隊列頭放入執行棧中,回到第一步。

 給你們一個我畫的圖,方便理解。

 不過你們可能會疑惑,事件循環機制跟觀察者模式哪有什麼關係?實際上是這樣的,在第2步中我寫道

當執行完一個異步任務就將其對應的回調函數放入任務隊列(Callback Queue)中。

 但咱們是如何判斷這個異步任務執行完了呢——觀察者模式。任務隊列是觀察者,WebAPIs是被觀察者,觀察者要求被觀察者當發生執行完異步任務這一事件時,通知他執行完了,並將該事件對應的回調函數傳過來。

js事件循環機制優缺點及與多線程的比較

 經過事件循環機制,咱們就能夠實現代碼的異步,從而不會堵塞線程。

 經過這一特性,

  1. js在IO上有着卓越的表現,由於IO操做再也不會堵塞住線程。
  2. 能夠作到高併發。稍微解釋一下爲何可以高併發——當同時有多個任務要執行,js將他一一排列起來,而後按順序執行,這樣cpu就不會由於同時要處理的工做太多而負載過大。

 樸靈在《深刻淺出nodeJS》中說道:

石器時代:同步。青銅時代:複製線程。白銀時代:多線程。黃金時代:事件驅動。

 不過我不敢說事件驅動就是比多線程好,但他確實沒有多線程的這些惱人的缺陷。

  1. 若是有大量的線程,會影響性能,由於操做系統須要在線程之間不停進行上下文切換。
  2. 一般數據是多個線程共享的,須要上鎖,同時又要防止出現死鎖現象。
  3. IO會堵塞住一個線程。

 但同時的,js也有他的缺陷。

  1. 不適合cpu密集型。也解釋一下——如一段代碼須要很是大量的計算量,以致於他長時間地佔着線程,這就堵塞了,後繼的同步代碼及異步代碼都沒法執行。不過,html5推出了web worker,能夠有效地解決這一缺陷,在本章不表,後面我會專門寫一篇文章來說他。
  2. 只能使用一個線程,沒法充分利用計算機的多核cpu。
  3. 可靠性低,一旦一個環節崩潰則整個程序所有崩潰。

 沒有一項技術是絕對完美的,但咱們要清楚他的優缺點及緣由,從而可以充分利用其優勢,同時規避其缺點甚至經過本身的方式解決其缺點。


參考資料

  1. Advantages and Disadvantages of a Multithreaded/Multicontexted Application: https://docs.oracle.com/cd/E13203_01/tuxedo/tux71/html/pgthr5.htm
  2. https://www.hostreview.com/blog/160311-the-pros-and-cons-of-using-nodejs: https://www.hostreview.com/blog/160311-the-pros-and-cons-of-using-nodejs
  3. 理解事件循環與任務隊列:https://www.jianshu.com/p/e865c3a7ba10
相關文章
相關標籤/搜索