深入了解Node.js中的一些特性
Node.js作為一門新興的后臺語言,旨在幫助程序員快速構(gòu)建可伸縮的應(yīng)用程序。Node.js有很多吸引人的地方,有關(guān)它的報道不計其數(shù),本文將針對EventEmitter、Streams、Coding Style、Linting、Coding Style等特性進(jìn)行分析探討,幫助用戶對Node.js有更深入的了解。
作為一個基于Chrome JavaScript 運行時建立的平臺,我們對JavaScript 的相關(guān)認(rèn)識,似乎都可應(yīng)用于node應(yīng)用程序之上;無需額外的語言擴展或修飾,我們便可以把前端編程的經(jīng)驗應(yīng)用于后端編程。
EventEmitter(事件發(fā)送器)
首先應(yīng)該先了解EventEmitter模型。它可以發(fā)送一個事件以及讓消費者訂閱感興趣的事件。我們可以把它看成是向一個異步函數(shù)進(jìn)行回調(diào)傳遞模式的擴展。特別是,當(dāng)需要進(jìn)行多次回調(diào)時,EventEmitter會更顯優(yōu)勢。
例如,有一個調(diào)用者向遠(yuǎn)程服務(wù)器發(fā)送了一個“列出文件”的請求。你可能想對返回結(jié)果進(jìn)行分組,對每一個分組進(jìn)行一次回調(diào)處理。EventEmitter模型可以讓你在每一個分組上發(fā)送“文件”回調(diào),當(dāng)全部操作完成時進(jìn)行“結(jié)束”處理。
使用EventEmitter時,只需設(shè)置好相關(guān)事件和參數(shù)。
var EventEmitter = require('events').EventEmitter;
var util = require('util');
function MyClass() {
if (!(this instanceof MyClass)) return new MyClass();
EventEmitter.call(this);
var self = this;
setTimeout(function timeoutCb() {
self.emit('myEvent', 'hello world', 42);
}, 1000);
}
util.inherits(MyClass, EventEmitter);
MyClass構(gòu)造函數(shù)創(chuàng)建了一個時間觸發(fā)器,觸發(fā)延時為1s,觸發(fā)事件為myEvent。要使用相關(guān)事件,需要執(zhí)行on()方法:
var myObj = new MyClass();
var start = Date.now();
myObj.on('myEvent', function myEventCb(str, num) {
console.log('myEvent triggered', str, num, Date.now() - start);
});
這里要注意的是,訂閱的EventEmitter事件雖然是異步事件,但當(dāng)時間觸發(fā)時,監(jiān)聽方的動作是會同步的。因此如果上述myEvent事件有10個監(jiān)聽者,所有的監(jiān)聽會被按次序調(diào)用而不用等候事件的循環(huán)。
如果EventEmitter的一個子類生成了一個emit(‘error')事件,但是無任何的監(jiān)聽方對此進(jìn)行訂閱,那么EventEmitter基類會拋出一個異常,從而導(dǎo)致當(dāng)執(zhí)行process對象時觸發(fā)uncaughtException事件。
verror
verror是基類Error的擴展,可以讓我們使用printf字符格式定義輸出消息。
Streams(流)
如果有一個非常龐大的文件需要處理,理想的方法應(yīng)該是讀一部分,寫一部分,不管文件有多大,只要時間允許,總會處理完成,這里就需要用到流的概念。Streams是Node中另一個廣泛使用的模型,在Node中是EventEmitter的實現(xiàn)。提供了可讀、可寫或者是全雙工接口。它是一個抽象接口,提供的常規(guī)操作事件包括:readable、writable、 drain、data、 end及close。如果我們能夠使用pipeline(管道)來對這些事件進(jìn)行有效整合,將可實現(xiàn)功能更強大的交互操作。
透過使用.pipe(),可以讓Note通過pipeline與back-pressure進(jìn)行通信。back-pressure的意思是:只讀取那些能夠?qū)懭氲?,或只寫入那些能夠讀取的。
例如我們現(xiàn)在把來自stdin的數(shù)據(jù)發(fā)送到一個本地文件和遠(yuǎn)程服務(wù)器:
var fs = require('fs');
var net = require('net');
var localFile = fs.createWriteStream('localFile.tmp');
net.connect('255.255.255.255', 12345, function(client) {
process.stdin.pipe(client);
process.stdin.pipe(localFile);
});
而如果我們想發(fā)送數(shù)據(jù)到一個本地文件,并想使用gunzip對這個stream進(jìn)行壓縮,可以這樣做:
var fs = require('fs');
var zlib = require('zlib');
process.stdin.pipe(zlib.createGunzip()).pipe(fs.createWriteStream('localFile.tar'));
如果想對stream有更多了解,請點擊這里。
Control Flow(流程控制)
由于JS中有第一類對象,閉包等功能概念,因而能夠容易地對回調(diào)權(quán)限進(jìn)行定義。這在進(jìn)行原型設(shè)計時是非常方便的,能夠?qū)壿嫏?quán)限按需進(jìn)行整合。但是同時容易造成使用笨拙的內(nèi)置函數(shù)。
例如我們想按次序讀入一系列文件,然后執(zhí)行某個任務(wù):
fs.readFile('firstFile', 'utf8', function firstCb(err, firstFile) {
doSomething(firstFile);
fs.readFile('secondFile', 'utf8', function secondCb(err, secondFile) {
doSomething(secondFile);
fs.readFile('thirdFile', 'utf8', function thirdCb(err, thirdFile) {
doSomething(thirdFile);
});
});
});
這個模式存在的問題是:
1.這些代碼的邏輯非常散亂無序,相關(guān)的操作流程難以理解。
2.沒有任何差錯或異常處理。
3.JS中閉包內(nèi)存泄漏是非常常見的,并難以診斷和探測。
如果我們想在一個輸入集上進(jìn)行一系列異步操作,使用一個流程控制庫是更明智的選擇。這里使用的是vasync。
vasync是一個流程控制庫,其思路來源于異步操作。它的特別之處是能夠讓消費者對某個任務(wù)處理過程進(jìn)行查看和觀察。這些信息對研究某個錯誤的產(chǎn)生過程是非常有用的。
Coding Style(編程風(fēng)格)
編程風(fēng)格可謂最具爭議性的話題,因為很多時候都是隨性的。蘿卜白菜,各有所愛。重要的是找到適合個人和團(tuán)隊的風(fēng)格。一些傳統(tǒng)的傳承或許能夠讓Node開發(fā)之旅變得更美好。
1.為函數(shù)命名
2.盡量對所有函數(shù)進(jìn)行命名。
3.避免閉包
4.不要在某個函數(shù)中定義其它函數(shù)。這可減少很多想不到的閉包內(nèi)存泄露意外。
5.更多和更小巧的函數(shù)
V8 JIT雖然是一個功能強大的引擎,但是更小巧和精簡的函數(shù)會與V8結(jié)合得更好。進(jìn)一步說,如果我們的函數(shù)都是小巧玲瓏的(100行左右),我們自己進(jìn)行閱讀和維護(hù)時也會感謝自己的。
用編程方式檢查風(fēng)格:保持風(fēng)格一致性,并使用一個檢查工具來加強。我們使用的是jsstyle。
Linting(代碼檢查)
Lint工具可以在不運行情況下進(jìn)行代碼的靜態(tài)分析,檢查出潛在的錯誤和風(fēng)險,例如在caseswitch中遺漏了break語句。Lint不簡單地等同于風(fēng)格檢查,它更針對于客觀的風(fēng)險分析,而不是主觀的風(fēng)格選擇。我們使用的javascriptlint,它里面有豐富檢查項目。
Logging(日志記錄)
當(dāng)我們進(jìn)行程序設(shè)計和編碼時,需要有長遠(yuǎn)的目光。特別是要考慮好使用什么工具來進(jìn)行調(diào)試。極好的第一步是進(jìn)行有效日志記錄。我們需要對信息進(jìn)行識別,看看什么是調(diào)試時特別留意的,什么是運行時用來分析研究的。這里推薦使用Bunyan,一個直接的Node.js日志記錄庫,數(shù)據(jù)輸出格式是JSON ,要了解更多信息,請點擊這里。
Client Server
如果一款應(yīng)用具備分布式處理能力,在市場上會更有吸引力。類似的接口可以用HTTP RESTFul API或原始的TCP JSON來描述。這可以讓開發(fā)者把Node上的經(jīng)驗與異步網(wǎng)絡(luò)環(huán)境相結(jié)合,以及把streams的使用與分布式可擴展式系統(tǒng)相結(jié)合。
常用工具:
1. restify
簡單來說,這是一個用于構(gòu)建REST服務(wù)的工具。它提供了良好的查看和調(diào)試處理支援,同時支持Bunyan與DTrace。
2. fast
fast是一款以TCP來處理JSON消息的輕量級工具。提供了DTrace支持,能夠讓我們迅速地對服務(wù)器客戶端進(jìn)行性能特征識別。
3. workflow
workflow構(gòu)建于restify之上,能夠?qū)σ幌盗羞h(yuǎn)程服務(wù)和API進(jìn)行業(yè)務(wù)流程定義。例如:錯誤狀態(tài),超時,重新連接,擁塞處理等。
相關(guān)文章
NodeJS?基于?Dapr?構(gòu)建云原生微服務(wù)應(yīng)用快速入門教程
Dapr?是一個可移植的、事件驅(qū)動的運行時,它使任何開發(fā)人員能夠輕松構(gòu)建出彈性的、無狀態(tài)和有狀態(tài)的應(yīng)用程序,并可運行在云平臺或邊緣計算中,它同時也支持多種編程語言和開發(fā)框架,本文重點介紹NodeJS云原生微服務(wù)應(yīng)用,感興趣的朋友一起看看吧2022-07-07
Node.js基礎(chǔ)入門之模塊與npm包管理器使用詳解
Node.js是一個基于Chrome?V8引擎的JavaScript運行時。類似于Java中的JRE,.Net中的CLR。本文將詳細(xì)為大家介紹Node.js中的模塊與npm包管理器的使用,需要的可以參考一下2022-03-03
深入理解Node.js中通用基礎(chǔ)設(shè)計模式
大家在談到設(shè)計模式時最先想到的就是 singletons, observers(觀察者) 或 factories(工廠方法)。本文重點給大家介紹Node.JS一些基礎(chǔ)模式的實現(xiàn)方法,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧2017-09-09
Node.js如何對SQLite的async/await封裝詳解
這篇文章主要給大家介紹了關(guān)于Node.js如何對SQLite的async/await進(jìn)行封裝的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02
Node.js學(xué)習(xí)教程之HTTP/2服務(wù)器推送【譯】
這篇文章主要給大家介紹了關(guān)于Node.js學(xué)習(xí)教程之HTTP/2服務(wù)器推送的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10

