這次帶給大家Node.js程式碼的執行原理,Node.js程式碼執行的注意事項有哪些,下面就是實戰案例,一起來看一下。
任何一個軟體下載安裝成功之後,其實只是一堆的機器碼,存在我們的電腦的硬碟當中,也就是我們所能看到的一堆的exe文件,當然,有的軟體比較大,可能會附的有一堆的dll檔案。
我們有兩種方式執行這個軟體:
大部分的軟體,像是QQ、飛秋、chrome瀏覽器,我們雙擊就可以執行運作起來。
有一部分的軟體,是需要在命令列裡面運行的,例如我們的node.js。
當一個軟體被執行的時候,我們的作業系統就會創建一個對應的進程,可以這麼理解,進程就是活的軟體。
進程與執行緒
電腦的核心是CPU,它承擔了所有的運算任務。它就像一座工廠,時刻在運行,假定工廠的電力有限,一次只能供給一個車間使用。也就是說,一個車間開工的時候,其他車間都必須停工。背後的意義就是,單一CPU一次只能運行一個任務。進程就好比工廠的車間,它代表CPU所能處理的單一任務,任一時刻,CPU總是運行一個進程,其他進程處於非運作狀態。在一個車間裡,可以有很多工人,他們協同完成一個任務。線程就好比車間裡的工人,一個進程可以包含多個線程。車間的空間是工人共享的,例如許多房間是每個工人都可以進出的,這象徵一個進程的內存空間是共享的,每個線程都可以使用這些共享內存。可是,每間房間的大小不同,有些房間最多能容納一個人,例如廁所,裡面有人的時候,其他的人就不能進去了。這代表一個執行緒使用某些共享記憶體時,其他執行緒必須等它結束,才能使用這塊記憶體。
一個防止他人進入的簡單方法,就是門口加一把鎖,先到的人鎖上門,後到的人看到上鎖,就在門口排隊,等鎖打開再進去,這叫互斥鎖。
為什麼JavaScript是單執行緒的?
JavaScript語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。那麼,為什麼JavaScript不能有多個執行緒呢?這樣能提高效率啊。
JavaScript的單線程,與它的用途有關。作為瀏覽器腳本語言,JavaScript的主要用途是與使用者互動,以及操作DOM。這決定了它只能是單線程,否則會帶來複雜的同步問題。例如,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上加入內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程為準?
所以,為了避免複雜性,從一誕生,JavaScript就是單線程,這已經變成了這門語言的核心特徵,將來也不會改變。
JavaScript是單線程的,雖然有worker、cluster等可以實現多線程,但是這些都是用來創建功能受限的線程,還是要受主線程的控制,所以,我們就認為JavaScript是單線程的。
另外,我們要明白,JavaScript是單線程的,不代表Node.js也是單線程的,Node.js運行了一個實例之後,是單進程多線程的,這裡面,有一個線程負責V8引擎解析JavaScript,有的執行緒負責libuv,也就是我們理解的事件循環。
如何理解IO:
I代表的是input,輸入:例如讀一個檔案、發起一個ajax請求資料
O代表的是output,輸出:例如寫檔案
同步vs 非同步
像下面這種,不涉及到讀寫檔案、網路請求的、計時器的,全部屬於同步程式碼:
var a = 1;var b = 2;function fn(){}
像下面這種都是屬於非同步程式碼:
fs.readFile('./a.js',callback); fs.writeFile('./b.js',callback);
包括我們在瀏覽器端看到的一堆的滑鼠、鍵盤事件、ajax等。
Node.js裡大部分的涉及異步的物件都是繼承自event.eventEmitter事件物件
#Node.js執行程式碼的機制
我們可以把Node .js進程想像成一個餐廳:
廚師代表的是JavaScript主線程,他不停的忙著把主線程上的同步程式碼執行下去,直到執行完為止。
//同步代码var a = 1;//同步代码var b = 2;//异步代码fs.readFile('./a.js',function(err,data){ if(err)throw data; });//同步代码var c = 3;//异步代码setTimeout(function(){ },200);//同步代码console.log(a + b + c);//同步代码function fn(){ console.log('abc'); }//异步代码fs.writeFile('./b.js',myData,function(err){ if(err)throw err; });
例如像上面的這樣的程式碼,從上往下開始執行,遇到同步程式碼,直接交給廚師去完成,但遇到了異步的程式碼的時候,這時候先不執行,而是類似來了新客人一樣,交給了我們的服務員,服務員就把這個客人點的菜登記在冊,相當於把這個非同步代碼看成了一個事件,記錄到了我們的事件隊列當中。
當差不多的時候,同步程式碼全部執行完了,這時候,主線程就空閒下來了,這時候,會啟動我們的事件循環線程,這個事件循環線程就會開始去查看服務員登記的有哪些客人點了菜,然後就會按先後的優先順序,開始一個客人一個客人的炒他們的菜,也就是執行我們的那些異步代碼,因為有很多的客人,每個客人的要求不太一樣,就像我們的Node.js程式碼,有各種各樣的非同步,像是計時器、讀取檔案、取檔案、網路請求,所以Node.js專門有一堆的線程池,處理這些非同步程式碼。
當執行緒池裡的某個執行緒負責的非同步執行完成了之後,相當於這個事件完成了,就會把這個事件對應的回呼函數往主執行緒後面進行排隊。
主線程上一旦有了新的要執行的程式碼,廚師一看有事情要做了,就開始工作了。
如果主執行緒上執行的回呼函數對應的程式碼,又產生了新的非同步程式碼,那又會登記到事件循環當中。
直到某個時刻,事件佇列裡所有的事件都被執行完畢,線程池慢慢的被清空了。
node.js就會呼叫process.exit(),作業系統銷毀目前的這個node.js進程。
理解到上面基本上就夠了,下面再深入一步:
我們到了學習網路程式設計那一塊的時候,我們知道,我們知道一個伺服器容器的時候,除非我們主動的使用CTRL + C把目前的這個進程中止掉,否則會一直的等待客戶端的連線並根據對應的路由進行對應的處理。
這個又怎麼理解呢?
我們回想一下我們jQuery裡面學的API,事件監聽有兩種方式:
one 一次監聽
on 無限監聽
我們可以這樣理解:大部分的文件操作、ajax之類的,其實只需要做一次就會結束,不會再有第二次,他們就等於one類型的事件模型。
但是如果涉及網路程式設計像socket,http之類的,則相當於on無限監聽。
相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!
推薦閱讀:
以上是Node.js程式碼的執行原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!