node事件循環(huán) EventEmitter 異步I/O Buffer緩沖區(qū) 模塊

node.js事件循環(huán)

node.js單進程走触,單線程的程序
每一個api都支持回調(diào)
所有的事件機制都是設(shè)計模式中的

一共是23種設(shè)計模式
http://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/observer.html
一個對象發(fā)生改變的時候,將自動通知其他對象,其他對象將相應(yīng)的做出反應(yīng)侄榴。發(fā)生改變的對象為觀察目標(biāo),被通知的對象為觀察者威恼。一個觀察目標(biāo)可以對應(yīng)多個觀察者竹祷,而這些觀察者之間沒有任何聯(lián)系擅编,可以根據(jù)需要增加觀察者爆阶,使得系統(tǒng)更加容易擴展,依賴關(guān)系為一對多沙咏,又被稱為模型-視圖模式

事件驅(qū)動程序

web server接收到請求辨图,將其關(guān)閉,進行處理肢藐,然后接著服務(wù)下一個web請求故河。
當(dāng)請求完成以后,放回處理隊列吆豹,當(dāng)?shù)竭_隊列開頭的時候鱼的,將其結(jié)果返回給用戶
即非阻塞式I/O 事件驅(qū)動I/O
會有一個主循環(huán)來監(jiān)聽事件理盆,當(dāng)檢測到事件以后,觸發(fā)回調(diào)函數(shù)

代碼

PS C:\Users\mingm\Desktop\test> node main.js
連接成功
數(shù)據(jù)接收成功
程序執(zhí)行完畢
PS C:\Users\mingm\Desktop\test>
// 引入一個 events 模塊
var events = require('events');
// 創(chuàng)建 eventEmitter對象
var eventEmitter = new events.EventEmitter();

// 創(chuàng)建connection事件的處理程序
var connectHandLer = function connected() {
    console.log('連接成功');

    // 觸發(fā) data_received 事件
    eventEmitter.emit('data_received');
};

// 綁定cinnection事件處理程序
eventEmitter.on('connection', connectHandLer);

// 綁定data_received事件凑阶,并完成處理程序的書寫
eventEmitter.on(
        'data_received',
        function() {
            console.log('數(shù)據(jù)接收成功');
        }
    );

// 觸發(fā) connection 事件
eventEmitter.emit('connection');

console.log('程序執(zhí)行完畢');

程序的執(zhí)行過程猿规,先完成各種綁定,觸發(fā)connection事件以后宙橱,尋找綁定的處理程序姨俩,為connected(),然后师郑,執(zhí)行一半环葵,又被觸發(fā),data_received事件宝冕。尋找綁定的處理程序张遭。一個匿名函數(shù),執(zhí)行地梨,事件全部完成菊卷,執(zhí)行最后一句,程序執(zhí)行完畢宝剖。
多用戶執(zhí)行的情況下洁闰,觸發(fā)事件以后,若處理程序正被其他用戶占用诈闺,排隊渴庆,直到前方全部處理完成以后铃芦,接著該用戶使用處理程序進行處理雅镊。

EventEmitter

node所有的異步I/O操作在完成的時候都會發(fā)送到一個事件到達事件隊列。node里的對象能夠分發(fā)事件
產(chǎn)生的事件的對象都是events.EventEmitter的實例

EventEmitter類

events模塊提供一個對象刃滓,它是對事件的觸發(fā)和事件的監(jiān)聽的封裝

PS C:\Users\mingm\Desktop\test> node main.js
事件觸發(fā)
PS C:\Users\mingm\Desktop\test>

過五秒后響應(yīng)

// event.js文件
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter(); // 創(chuàng)建一個event對象
event.on('some_event', function(){console.log('事件觸發(fā)');});
setTimeout(function(){event.emit('some_event');}, 5000);

大概解釋一下這段代碼
前兩句很簡單仁烹,后兩句說一下,event的對象注冊了一個事件的監(jiān)聽器咧虎。這個事件的監(jiān)聽器為一個匿名函數(shù)卓缰,事件名稱為some_event,當(dāng)5000秒以后被觸發(fā)先對象event發(fā)送一個事件some_event觸發(fā)了匿名函數(shù)即監(jiān)聽器砰诵,監(jiān)聽器被執(zhí)行征唬。
其中EventEmitter的每個事件由一個事件名和若干參數(shù)組成,對于一個事件能有若干的監(jiān)聽器茁彭,當(dāng)事件觸發(fā)的時候总寒,監(jiān)聽器會被依次調(diào)用,事件參數(shù)作為回調(diào)函數(shù)的參數(shù)進行傳遞理肺,需要注意的是摄闸,監(jiān)聽器會被依次調(diào)用

error事件

error是一個單獨列出來的事件善镰,一般要為其綁定一個監(jiān)聽器,因為node如果拋出error年枕,若沒有監(jiān)聽器執(zhí)行炫欺,將會直接退出執(zhí)行,并返回錯誤

Buffer緩沖區(qū)

處理TCP或者文件流的時候熏兄,需要用到二進制的數(shù)據(jù)品洛,定義了一個Buffer類,該類用于專門存放二進制數(shù)據(jù)的緩沖區(qū)

Buffer與字符編碼

const buf = Buffer.from('ming', 'ascii');   // 聲明一個只讀的變量

console.log(buf.toString('hex'));

console.log(buf.toString('utf-8'));
PS C:\Users\mingm\Desktop\test> node main.js
6d696e67
ming
PS C:\Users\mingm\Desktop\test>

創(chuàng)建一個buffer類,并完成讀取寫入

PS C:\Users\mingm\Desktop\test> node main.js
23456789:;<=>?@ABCDEFGHIJK
23456789:;<=>?@ABCDEFGHIJK
PS C:\Users\mingm\Desktop\test>
buf = Buffer.alloc(26);
for(var i = 0; i < 26; i++){
    buf[i] = 50 + i;
};
console.log(buf.toString('ascii'));
console.log(buf.toString('utf8'));

將buffer轉(zhuǎn)換為jsoon

PS C:\Users\mingm\Desktop\test> node main.js
{"type":"Buffer","data":[1,2,3,4,5]}
PS C:\Users\mingm\Desktop\test>
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);

console.log(json);

將JSON轉(zhuǎn)換為Buffer

一個轉(zhuǎn)換方法JSON.parse

> JSON.parse('{"1": 3, "2": [4, 5, 6]}', function (k, v) {
...     console.log(k); // 輸出當(dāng)前的屬性名霍弹,從而得知遍歷順序是從內(nèi)向外的毫别,
...     console.log("----------");              // 最后一個屬性名會是個空字符串。
...     console.log(v);
... });
1
----------
3
0
----------
4
1
----------
5
2
----------
6
2
----------
[ <3 empty items> ]

----------
{}
undefined
>

開始轉(zhuǎn)換

const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);

console.log(json);


const copy = JSON.parse(    // 調(diào)用JSON.parse函數(shù)典格,將字符串轉(zhuǎn)換為對象岛宦,后一個參數(shù)為轉(zhuǎn)換的函數(shù),轉(zhuǎn)換的時候?qū){(diào)用
        json,
        (key, value) => {   // 此為es6的語法耍缴,一個匿名函數(shù)
            console.log(value, '----', !!value, '----', value.type, '-----', value.data);
            return value && value.type === 'Buffer' ? Buffer.from(value.data): value;   // 只留下最后一個進行轉(zhuǎn)換砾肺,前面的轉(zhuǎn)換全部被覆蓋
        }
    )

// 輸出: <Buffer 01 02 03 04 05>
console.log(copy);
PS C:\Users\mingm\Desktop\test> node main.js
{"type":"Buffer","data":[1,2,3,4,5]}
Buffer ---- true ---- undefined ----- undefined
1 '----' true '----' undefined '-----' undefined
2 '----' true '----' undefined '-----' undefined
3 '----' true '----' undefined '-----' undefined
4 '----' true '----' undefined '-----' undefined
5 '----' true '----' undefined '-----' undefined
[ 1, 2, 3, 4, 5 ] '----' true '----' undefined '-----' undefined
{ type: 'Buffer', data: [ 1, 2, 3, 4, 5 ] } '----' true '----' 'Buffer' '-----' [ 1, 2, 3, 4, 5 ]
<Buffer 01 02 03 04 05>
PS C:\Users\mingm\Desktop\test>

Buffer的合并

var buffer1 = Buffer.from('222');
var buffer2 = Buffer.from('3333');
var buffer3 = Buffer.concat([buffer1, buffer2]);
console.log(buffer3.toString());

Stream流

流為一個抽象的接口,

從流中讀取數(shù)據(jù)

PS C:\Users\mingm\Desktop\test> node main.js
end防嗡!
33333333333333333333333333
PS C:\Users\mingm\Desktop\test>
var fs = require('fs');
var data = '';

// 創(chuàng)建可讀流
var readerStream = fs.createReadStream('input.txt');

// 設(shè)置編碼為 utf8
readerStream.setEncoding('UTF8');

// 處理流事件 data事件
readerStream.on('data', (chunk) => {data += chunk;});   // 遇到數(shù)據(jù)讀取变汪,將讀取到的內(nèi)容賦值給data

// 處理流事件 end事件
readerStream.on('end', () => {console.log(data);}); // 將讀取到的保存到內(nèi)存中的數(shù)據(jù)打印出來

// 處理事件 error
readerStream.on('error', (err) => {console.log(err.stack);});   // 處理error事件,將錯誤輸出蚁趁,避免程序的運行中斷

console.log('end裙盾!');

寫入流

PS C:\Users\mingm\Desktop\test> node main.js
程序執(zhí)行完畢
寫入完成
PS C:\Users\mingm\Desktop\test>
var fs = require('fs');
var data = '這是一段示例';

// 創(chuàng)建一個可以寫入的流,寫入到文件output.txt中
var writerStream = fs.createWriteStream('output.txt');

// 使用 utf8 編碼寫入數(shù)據(jù)
writerStream.write(data, 'UTF8');

// 標(biāo)記文件末尾
writerStream.end();

// 處理流事件 --> data, end, add error
writerStream.on('finish', () => {console.log('寫入完成');});

writerStream.on('error', () => {console.log(err.stack);});

console.log('程序執(zhí)行完畢');


管道流

把兩個文件之間建立流他嫡,讓一個文件的數(shù)據(jù)流向另外一個文件

PS C:\Users\mingm\Desktop\test> node main.js
程序執(zhí)行完畢
PS C:\Users\mingm\Desktop\test>
var fs = require('fs');

// 創(chuàng)建一個可讀流
var readerStream = fs.createReadStream('input.txt');

// 創(chuàng)建一個可寫流
var writerStream = fs.createWriteStream('output.txt');

// 管道讀寫操作
// 將兩個流通過管道連接起來
readerStream.pipe(writerStream);

console.log('程序執(zhí)行完畢');

這里需要注意的是番官,由于流的影響,導(dǎo)致在操作的時候钢属,會覆蓋掉要寫入文件的內(nèi)容徘熔,原先的內(nèi)容會被覆蓋

鏈?zhǔn)搅?/h3>
PS C:\Users\mingm\Desktop\test> node main.js
文件壓縮完成
PS C:\Users\mingm\Desktop\test>
var fs = require('fs');
var zlib = require('zlib');

// 壓縮 input.txt文件為 input.txt.gz
fs.createReadStream('input.txt')    // 創(chuàng)建一個可讀流
    .pipe(zlib.createGzip())        // 將創(chuàng)建的可寫流和壓縮流連接
    .pipe(fs.createWriteStream('input.txt.gz'));    // 在創(chuàng)建可寫流,將三個流連接到一起

console.log('文件壓縮完成');

內(nèi)容未被覆蓋的寫入

var fs = require('fs');
var read = fs.createReadStream('output.txt');
var write = fs.createWriteStream('input.txt');
read.pipe(write);
console.log('執(zhí)行完畢');

具體詳細淆党,請查文檔
https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
https://nodejs.org/api/fs.html#fs_file_system_flags
文檔寫的很詳細酷师,后面的參數(shù)為一個對象,通過對象即可進行修改

模塊

模塊之前已經(jīng)闡述過一部分染乌,這里闡述服務(wù)器端的模塊

服務(wù)器端的模塊

node中自帶了一個http模塊山孔,在代碼中請求他,并將返回值賦值給本地變量荷憋。即本地變量變成了一個擁有所有http模塊所提供的公共方法的對象台颠。
node中有四個模塊,(原生模塊和三種文件模塊)

從文件模塊的緩存中添加

原生模塊和文件模塊優(yōu)先級不同台谊,都會優(yōu)先從文件模塊的緩存中加載以及存在的模塊

從原生模塊加載

原生模塊的優(yōu)先級低于文件模塊緩存的優(yōu)先級蓉媳。原生模塊也有緩存區(qū)

從文件加載

這個上一篇以及闡述完成譬挚,不在闡述

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市酪呻,隨后出現(xiàn)的幾起案子减宣,更是在濱河造成了極大的恐慌,老刑警劉巖玩荠,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漆腌,死亡現(xiàn)場離奇詭異,居然都是意外死亡阶冈,警方通過查閱死者的電腦和手機闷尿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來女坑,“玉大人填具,你說我怎么就攤上這事〈移” “怎么了劳景?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碉就。 經(jīng)常有香客問我盟广,道長,這世上最難降的妖魔是什么瓮钥? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任筋量,我火速辦了婚禮,結(jié)果婚禮上碉熄,老公的妹妹穿的比我還像新娘桨武。我一直安慰自己,他們只是感情好具被,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布玻募。 她就那樣靜靜地躺著只损,像睡著了一般一姿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上跃惫,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天叮叹,我揣著相機與錄音,去河邊找鬼爆存。 笑死蛉顽,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的先较。 我是一名探鬼主播携冤,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼悼粮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了曾棕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎束析,沒想到半個月后滚粟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡衙耕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年昧穿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片橙喘。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡时鸵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厅瞎,到底是詐尸還是另有隱情寥枝,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布磁奖,位于F島的核電站囊拜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏比搭。R本人自食惡果不足惜冠跷,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望身诺。 院中可真熱鬧蜜托,春花似錦、人聲如沸霉赡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽穴亏。三九已至蜂挪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嗓化,已是汗流浹背棠涮。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留刺覆,地道東北人严肪。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親驳糯。 傳聞我的和親對象是個殘疾皇子篇梭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容