關於nodejs的異步I/O模型!JavaScript事件機制

知識前提:

瞭解之前,需要掌握一下知識
js引擎執行機制
參考
JS的執行環境是單線程的,(這個線程就是瀏覽器的JS引擎),一次只能執行做一件事情;
瀏覽器內核實現了多個線程異步執行,這些線程在內核的控制下相互配合從而保持同步

瀏覽器的線程:

瀏覽器的工作原理:

js引擎是事件驅動的,它一直在等待着任務任務隊列中的任務到來,又由於js是單線程的,所以很對任務會出現排隊的情況

參考這裏

雖然JavaScript是單線程執行的,但是瀏覽器並不是單線程執行的,它們有JavaScript的執行線程、UI節點的渲染線程,圖片等資源的加載線程,以及Ajax請求線程等。在Chrome設計中,爲了防止因一個Tab window的奔潰而影響整個瀏覽器,它的每一個Tab被設計爲一個進程;在Chrome設計中存在很多的進程,並利用進程間通訊來完成它們之間的同步,因此這也是Chrome快速的法寶之一。對於Ajax的請求也需要特殊線程來執行,當需要發送一個Ajax請求的時候,瀏覽器會開闢一個新的線程來執行HTTP的請求,它並不會阻塞JavaScript線程的執行,HTTP請求狀態變更事件會被作爲回調放入到瀏覽器的事件隊列中等待被執行。

nodejs事件循環機制
參考


(全局任務屬於宏任務)
1、如果當前任務能夠直接執行,那麼直接執行
2、如果是宏任務,則放入宏任務隊列中
3、如果是微任務,則放入爲任務隊列中
4、當執行棧爲空了之後,執行整個爲微任務隊列
5、但執行棧又爲空了之後,則執行整個宏任務隊列

引自這裏

雖然JavaScript是單線程執行的,但是瀏覽器並不是單線程執行的,它們有JavaScript的執行線程、UI節點的渲染線程,圖片等資源的加載線程,以及Ajax請求線程等。在Chrome設計中,爲了防止因一個Tab window的奔潰而影響整個瀏覽器,它的每一個Tab被設計爲一個進程;在Chrome設計中存在很多的進程,並利用進程間通訊來完成它們之間的同步,因此這也是Chrome快速的法寶之一。對於Ajax的請求也需要特殊線程來執行,當需要發送一個Ajax請求的時候,瀏覽器會開闢一個新的線程來執行HTTP的請求,它並不會阻塞JavaScript線程的執行,HTTP請求狀態變更事件會被作爲回調放入到瀏覽器的事件隊列中等待被執行。

函數執行棧

我們的js代碼從上到下的執行,當一個函數被執行的時候,都會有一個執行上下文,全局環境也有一個執行上下文,就是全局的上下文。JavaScript將以棧的形式來存儲他們。每執行一個函數,就把它上下文存入棧。棧的最底層就是全局上下文,棧頂就是當前正在執行的函數。每當一個函數執行結束,他的執行上下文就從棧中被彈出,釋放。最底層的全局上下文,在瀏覽器關閉的時候才被彈出。

任務隊列

macro-task(task):

  • setTimeout/setInterval
  • setImmediate
  • I/O操作
  • UI rendering

micro-task(job):

  • process.nextTick
  • Promise
  • MutationObserve

JavaScript執行的機制是:首先執行調用棧中的函數,當調用棧中的執行上下文全部被彈出,只剩下全局上下文的時候,就開始執行job的執行隊列,job的執行完以後就開始執行task的隊列中的。先進入的先執行,後進入的後執行。無論是task還是job都是通過函數調用棧來執行。task執行完成一個,js代碼會繼續檢查是否有job需要執行(就像是一個優先級的問題)。就形成了task-job-task-job的循環(其實這裏可以將第一次的函數調用棧也看成一個task)。這就形成了event loop.

異步I/O

現在CPU快了,但是IO一般比較慢,是很多操作的瓶頸,很容易組賽,異步能夠緩解這種阻塞。
雖然現在的語言/平臺基本都能做到異步,但是nodejs歷害之處能在一個線程裏面異步操作,而其它語言遇到一個請求就需要再開一個線程,在打兵發的時候就很容易吃不消!!!
阻塞I/O:比如你要讀一個文件,整個線程都暫停下來,等到文件讀完了之後再繼續執行。也就是I/O操作阻塞了代碼的執行,極大地降低了程序的效率
下面感受一下怎麼用Node.js實現非阻塞I/O,以讀取文件爲例:

var fs = require("fs");
fs.readFile("./testfile", "utf8", function(error, file) {  
     if (error) throw error;  
     console.log("我讀完文件了!");
});
console.log("我不會被阻塞!");


代碼中給readFile綁定了一個回掉函數,,並且在讀完了testfile之後執行回掉函數,期間後面的代碼是可以繼續執行的。其實node的異步I/O就是這麼簡單嘿嘿嘿!

 

一隻可愛的小前端~~


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章