node事件循環和消息隊列簡單分析

node的好處毋庸置疑,事件驅動,異步非阻塞I/O,以及處理高併發的能力深刻人心,所以你們喜歡用node作一些小型後臺服務或者做爲中間層和其餘服務配合完成一些大型應用場景。node

 

什麼是異步?web


異步和同步應該是常常談的一個話題了。同步的概念很簡單,自上而下依次執行,必須等上邊執行完下邊纔會執行。而異步能夠先提交一個命令,中間能夠去執行別的事務,而當執行完以後回過頭來返回以前的任務。ajax

舉個栗子:數據庫

你很幸運,找了一個漂亮的女友,有一天你的女友發短信問你晚上看什麼電影?但你並不知道看什麼,立刻打開電腦查了一下近期熱播的電影,這其中你女友一直在等你,這就是同步編程

而異步呢?仍是你女友發短信問你看什麼電影,你跟她說: 你先等會吧吧,等我查一下,查好以後你回頭打電話告訴了她。這就是異步。瀏覽器

從而咱們能看出同步和異步的一些特色:網絡

1.必須發生在兩個對象身上。(你和你女友)數據結構

2.必須發生一些事情。(看電影)多線程

不一樣的就是:同步就是依次執行,執行完一個以後在執行另外一個,直到執行完成,而異步就是先執行一個,可能沒有執行完成再去執行另外一個,等第一個執行完成後再返回結果併發

 

那麼,爲何須要異步呢?

 

答案很明顯,爲了提升辦事的效率,CPU計算速度和磁盤的讀寫速度差太遠了,磁盤供不該求,所以有了計算機的存儲系統的分層設計,平衡了效率和成本。能夠說懶惰推進人類的進步,任何能夠下降花費時間而達到同等功效的方法確定會被優先採用。

發送短信時等待對方回覆的時間純粹的浪費掉了,CPU寫入磁盤等待返回的結果的等待時間也被無情的消耗了,這是一個講究效率的時代徹底不能忍受的,所以讓員工一直處於忙碌狀態,最大限度的榨取員工價值是老闆追求的,讓CPU和磁盤都不停的滿

負荷處理事務也是效率須要的。所以,異步處理出現了。

 

那什麼是異步IO?

 

異步IO是指操做系統提供的IO(數據進出)的能力,好比鍵盤輸入,對應到顯示器上會有專門的數據輸出接口,這就是咱們生活中可見的IO能力;這個接口在向下會進入到操做系統這個層面,在操做系統中,會提供諸多的能力,好比:磁盤的讀寫,DNS

的查詢,數據庫的鏈接啊,網絡請求的處理,等等;

在不一樣的操做系統層面,表現的不一致。有的是異步非阻塞的;有的是同步的阻塞的,不管如何,咱們均可以看作是上層應用於下層系統之間的數據交互;上層依賴於下層,可是反過來,上層也能夠對下層提供的這些能力進行改造;若是這種操做是異步

的,非阻塞的,那麼這種就是異步非阻塞的異步IO模型;若是是同步的阻塞的,那麼就是同步IO模型;

koa就是一個上層的web服務框架,所有由js實現,他有操做系統之間的交互,所有經過nodejs來實現;如nodejsreadFile就是一個異步非阻塞的接口,readFileSync就是一個同步阻塞接口。

 

什麼是事件循環?

事件循環是指 Node.js執行非阻塞I/O操做,儘管JavaScript是單線程的,但因爲大多數內核都是多線程的,node.js會盡量將操做裝載到系統內核。所以它們能夠處理在後臺執行的多個操做。當其中一個操做完成時,內核會告訴Node.js,以便node.js可
 
以將相應的回調添加到輪詢隊列中以最終執行。也就是說,js是單線程的,可是node運行的時候實際上是多線程的。(我的理解)
 
而消息隊列是一個先進先出的隊列,它裏面存放着各類消息。
 
V8引擎
 
咱們常說的Chrome引擎和nodejs引擎就是V8引擎,他大體由如下組成:

 

這個引擎由內存堆和調用棧組成,內存堆就是負責進行內存分配,好比變量賦值,調用棧就是代碼執行的地方。

調用棧中順序執行主線程的代碼,當調用棧中爲空時,js引擎會去消息隊列取消息。取到後就執行。JavaScript是單線程的編程語言,意味着它有一個單一的調用棧。所以它只能在同一時間作一件事情。調用棧是一種數據結構,它基本上記錄了咱們
在程序中的什麼位置。若是咱們步入一個函數中,咱們會把這些數據放在堆棧的頂部。若是咱們從一個函數中返回,這些數據將會從棧頂彈出。這就是堆棧的用途。調用棧中的每一個條目叫作棧幀。
 
堆和棧的區別就是先進先出,一個先進後出。
 
當js運行時:
 

 


咱們常用的一些API並非js引擎中提供的,例如定時器setTimeout。

它們實際上是在瀏覽器中提供的,也就是運行時提供的,所以,實際上除了JavaScript引擎之外,還有其餘的組件。

其中有個組件就是由瀏覽器提供的,叫Web APIs,像DOM,AJAX,setTimeout等等。

而後還有就是很是受歡迎的事件循環和回調隊列。

運行時負責給引擎線程發送消息,只負責生產消息,不負責取消息。

 

消息隊列

 

主線程在執行過程當中遇到了異步任務,就發起函數或者稱爲註冊函數,經過event loop線程通知相應的工做線程(如ajax,dom,setTimout等),同時主線程繼續向後執行,不會等待。等到工做線程完成了任務,eventloop線程會將消息添加到消息隊列

中,若是此時主線程上調用棧爲空就執行消息隊列中排在最前面的消息,依次執行。

新的消息進入隊列的時候,會自動排在隊列的尾端。

 

單線程意味着js任務須要排隊,若是前一個任務出現大量的耗時操做,後面的任務得不到執行,任務的積累會致使頁面的「假死」。這也是js編程一直在強調須要迴避的「坑」。

主線程會循環上述步驟,事件循環就是主線程重複從消息隊列中取消息、執行的過程。

須要注意的是 GUI渲染線程與JS引擎是互斥的,當JS引擎執行時GUI線程會被掛起,GUI更新會被保存在一個隊列中等到JS引擎空閒時當即被執行。所以頁面渲染都是在js引擎主線程調用棧爲空時進行的。

 

其實 事件循環 機制和 消息隊列 的維護是由事件觸發線程控制的。

事件觸發線程 一樣是瀏覽器渲染引擎提供的,它會維護一個 消息隊列。

JS引擎線程遇到異步(DOM事件監聽、網絡請求、setTimeout計時器等...),會交給相應的線程單獨去維護異步任務,等待某個時機(計時器結束、網絡請求成功、用戶點擊DOM),而後由 事件觸發線程 將異步對應的 回調函數 加入到消息隊列中,消

息隊列中的回調函數等待被執行。

同時,JS引擎線程會維護一個 執行棧,同步代碼會依次加入執行棧而後執行,結束會退出執行棧。

若是執行棧裏的任務執行完成,即執行棧爲空的時候(即JS引擎線程空閒),事件觸發線程纔會從消息隊列取出一個任務(即異步的回調函數)放入執行棧中執行。

 

(以上是我我的理解還有從網上的摘抄[dog])

相關文章
相關標籤/搜索