Node.js 非同步模式淺析

來源:互聯網
上載者:User

標籤:style   blog   http   color   io   os   使用   ar   for   

注:此文是node.js實戰讀後的總結。

在平常的指令碼語言中都是同步進行的,比如php,伺服器處理多個請求的方法就是並行這些指令碼。多任務處理,多線程等等。但是這種處理方式也有一個問題:每一個進程或者線程都會耗費大量的系統資源。如果有一種方法可以最大化的利用CPU的計算能力和可用記憶體以減少資源浪費那就極好了。這樣,我們的node.js就應運而生了。

 

上一個node.js最簡單的非同步編程案例:

 1 var fs = require(‘fs‘); 2  3 var file; 4  5 fs.open( 6     ‘info.txt‘,‘r‘, 7     function(err,handle){ 8         var buf = new Buffer(100000); 9     fs.read(10     handle,buf,0,100000,null,11     function(err,length){12         console.log(buf.toString(‘utf8‘,0,length));13         fs.close(handle,function(){});14     });15         16     }17 );
View Code

從這個例子我們就可以看到在非同步函數中使用的最多的回呼函數,這些回呼函數至少包含一個參數,即最後操作的狀態(成功還是失敗),一般而言還有第二個參數,即最後操作返回的結果或資訊(比如檔案控制代碼,資料庫連接,查詢到的資料等),一些回呼函數可能還包含更多的參數.假設err代表返回的狀態參數,則該參數的值一般會有以下幾種情況:1.null:表示操作成功,並且會有一個傳回值(如果你需要的話).2.一個error對象的執行個體:通常人們習慣在error對象上添加code欄位並且用message欄位來儲存錯誤資訊(注:這種方式可以讓我們寫出的非阻塞代碼更具可控性)。對上面的代碼進行最佳化加上錯誤處理:

 1 var fs = require(‘fs‘); 2  3 var file; 4  5 fs.open( 6     ‘info.txt‘,‘r‘, 7     function(err,handle){ 8         //第一種錯誤處理方式 9         if(err)10         {11             console.log("ERROR:"+ err.code + "("+err.message+")");12             return;13         }14         var buf = new Buffer(100000);15     fs.read(16     handle,buf,0,100000,null,17     function(err,length){18         //第二種錯誤處理方式19         if(err){20              console.log("ERROR:"+err.code+"("+err.message+")");21         }else{22             console.log(buf.toString(‘utf8‘,0,length));23             fs.close(handle,function(){});24         }25         26     });27         28     }29 );
View Code

但是,在非同步處理的過程中得注意this的用法以及函數範圍的變化,看下面的代碼:

 1 var fs = require(‘fs‘); 2  3 function FileObject(){ 4     this.filename = ‘‘; 5  6     this.file_exists = function(callback){ 7         console.log("About to open:"+this.filename); 8         fs.open(this.filename,‘r‘,function(err,handle){ 9             if(err){10                 console.log("Can‘t open:"+ this.filename);11                 callback(err);12                 return;13             }14             fs.close(handle,function(){});15             callback(null,true);16         });17     };18 }19 20 var fo = new FileObject();21 fo.filename = ‘info‘;22 fo.file_exists(function(err,results){23     if(err){24         console.log("ERROR:"+err.code+"("+err.message+")");25         return;26     }27     console.log("file exists!!!");28 });
View Code

我們原本以為輸出的應該是:

About to open:info

Can‘t open:info

但是實際上確實:

About to open:info

Can‘t open:undefined

ERROR:ENOENT(ENOENT, open ‘G:\nodejs\info‘)

這是為什麼呢?在我們的理解中,大多數情況下,當一個函數嵌套在另一個函數中時,他就會自動繼承父函數的範圍,因而就能訪問所有的變數了。但是為什麼我們嵌套的回呼函數卻沒有出現我們以為的輸出呢?

這個問題得歸結於this關鍵字和非同步回呼函數本身。在我們調用fs.open函數的時候,他會先初始化自己,然後調用底層的作業系統函數(在我們的代碼中,就是開啟檔案),並且把回呼函數插入到node.js的事件隊列中去,執行完會立即返回給file_exists函數,然後退出。當fs.open完成任務後,node就會調用該回呼函數,但此時,該函數已經不再擁有FileObject這個類的繼承關係了,所以回呼函數會重新賦予新的this指標,在這一過程中我們就丟失了我們的FileObject的this指標,故我們就不能訪問我們的file_name。但是在這個過程中,回呼函數的範圍還保留著。這裡關係到nodejs的事件模式(參考資料:http://nodejs.org/docs/latest/api/events.html),這個也是nodejs的一個重要特性,我們現在不多說。這種錯誤最常見的解決方案就是把消失的this指標儲存在變數中,下面我們來重寫

 1 this.file_exists = function(callback){ 2         //用一個變數來儲存this指標 3         var self = this; 4         console.log("About to open:"+self.filename); 5         fs.open(this.filename,‘r‘,function(err,handle){ 6             if(err){ 7                 console.log("Can‘t open:"+ self.filename); 8                 callback(err); 9                 return;10             }11             fs.close(handle,function(){});12             callback(null,true);13         });14     };
View Code

 

好了,這樣我們的輸出就和我們想象中的一樣了,我們在寫代碼時一定不要忘了this的變化,不然就可能出現很多bug = = .

 

好,我們接著說.我們都知道Node運行在單線程中,使用事件輪詢來調用外部函數和服務。它將回呼函數插入事件隊列中來等待響應,並且儘快執行回呼函數。好,下面我們來看一個函數,這個函數的功能就是計算兩個數組的交叉元素:

 1 function compute_intersection(arr1,arr2,callback){ 2     var results = []; 3     for(var i = 0; i<arr1.length; i++) 4         for(var j = 0; j<arr2.length; j++){ 5             if(arr1[i] == arr2[j]){ 6                 results.push(arr1[i]); 7                 bareak; 8             } 9         }10     callback(null,true);11 }
View Code

當我數組中的元素特別大時,該函數就會耗費大量的計算時間。我們知道在單線程模式中,node.js在同一時間只能做一件事,所以這耗費的大量時間將會成為一個問題。而比如其他一些計算雜湊。摘要(digest)或者其他一下耗費時間的操作就可能導致應用處於假死的狀態。所以說node.js並不適合計算服務器,nodejs更適合常見的網路任務,比如那些需要大量I、O或者需要向其他服務要求的任務,如果需要一個大量計算的伺服器,第一個解決方案就是把這些操作遷移到其他伺服器上去,然後用nodejs遠程調用。但如果只是偶爾執行這種任務,那麼還有第二種解決方案,那就是利用全域對象process的nextTick方法,該方法的作用就是告訴系統,我不要執行控制權,你在你閒置時候執行我給你的函數就行了.

 

好,非同步中的一些陷井和基本已經介紹完了,歡迎大家補充。

 

Node.js 非同步模式淺析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.