天天看點

node事件循環 EventEmitter 異步I/O Buffer緩沖區 子產品node.js事件循環EventEmitterBuffer緩沖區子產品

node.js事件循環

node.js單程序,單線程的程式

每一個api都支援回調

所有的事件機制都是設計模式中的

一共是23種設計模式 http://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/observer.html

一個對象發生改變的時候,将自動通知其他對象,其他對象将相應的做出反應。發生改變的對象為觀察目标,被通知的對象為觀察者。一個觀察目标可以對應多個觀察者,而這些觀察者之間沒有任何聯系,可以根據需要增加觀察者,使得系統更加容易擴充,依賴關系為一對多,又被稱為模型-視圖模式

事件驅動程式

web server接收到請求,将其關閉,進行處理,然後接着服務下一個web請求。

當請求完成以後,放回處理隊列,當到達隊列開頭的時候,将其結果傳回給使用者

即非阻塞式I/O 事件驅動I/O

會有一個主循環來監聽事件,當檢測到事件以後,觸發回調函數

代碼

PS C:\Users\mingm\Desktop\test> node main.js
連接配接成功
資料接收成功
程式執行完畢
PS C:\Users\mingm\Desktop\test>           
// 引入一個 events 子產品
var events = require('events');
// 建立 eventEmitter對象
var eventEmitter = new events.EventEmitter();

// 建立connection事件的處理程式
var connectHandLer = function connected() {
    console.log('連接配接成功');

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

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

// 綁定data_received事件,并完成處理程式的書寫
eventEmitter.on(
        'data_received',
        function() {
            console.log('資料接收成功');
        }
    );

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

console.log('程式執行完畢');           

程式的執行過程,先完成各種綁定,觸發connection事件以後,尋找綁定的處理程式,為connected(),然後,執行一半,又被觸發,data_received事件。尋找綁定的處理程式。一個匿名函數,執行,事件全部完成,執行最後一句,程式執行完畢。

多使用者執行的情況下,觸發事件以後,若處理程式正被其他使用者占用,排隊,直到前方全部處理完成以後,接着該使用者使用處理程式進行處理。

EventEmitter

node所有的異步I/O操作在完成的時候都會發送到一個事件到達事件隊列。node裡的對象能夠分發事件

産生的事件的對象都是events.EventEmitter的執行個體

EventEmitter類

events子產品提供一個對象,它是對事件的觸發和事件的監聽的封裝

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

過五秒後響應

// event.js檔案
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();    // 建立一個event對象
event.on('some_event', function(){console.log('事件觸發');});
setTimeout(function(){event.emit('some_event');}, 5000);
           

大概解釋一下這段代碼

前兩句很簡單,後兩句說一下,event的對象注冊了一個事件的監聽器。這個事件的監聽器為一個匿名函數,事件名稱為some_event,當5000秒以後被觸發先對象event發送一個事件some_event觸發了匿名函數即監聽器,監聽器被執行。

其中EventEmitter的每個事件由一個事件名和若幹參數組成,對于一個事件能有若幹的監聽器,當事件觸發的時候,監聽器會被依次調用,事件參數作為回調函數的參數進行傳遞,需要注意的是,監聽器會被依次調用

error事件

error是一個單獨列出來的事件,一般要為其綁定一個監聽器,因為node如果抛出error,若沒有監聽器執行,将會直接退出執行,并傳回錯誤

Buffer緩沖區

處理TCP或者檔案流的時候,需要用到二進制的資料,定義了一個Buffer類,該類用于專門存放二進制資料的緩沖區

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>           

建立一個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轉換為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轉換為Buffer

一個轉換方法JSON.parse

> JSON.parse('{"1": 3, "2": [4, 5, 6]}', function (k, v) {
...     console.log(k); // 輸出目前的屬性名,進而得知周遊順序是從内向外的,
...     console.log("----------");              // 最後一個屬性名會是個空字元串。
...     console.log(v);
... });
1
----------
3
0
----------
4
1
----------
5
2
----------
6
2
----------
[ <3 empty items> ]

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

開始轉換

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

console.log(json);


const copy = JSON.parse(    // 調用JSON.parse函數,将字元串轉換為對象,後一個參數為轉換的函數,轉換的時候将會調用
        json,
        (key, value) => {    // 此為es6的文法,一個匿名函數
            console.log(value, '----', !!value, '----', value.type, '-----', value.data);
            return value && value.type === 'Buffer' ? Buffer.from(value.data): value;    // 隻留下最後一個進行轉換,前面的轉換全部被覆寫
        }
    )

// 輸出: <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流

流為一個抽象的接口,

從流中讀取資料

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

// 建立可讀流
var readerStream = fs.createReadStream('input.txt');

// 設定編碼為 utf8
readerStream.setEncoding('UTF8');

// 處理流事件 data事件
readerStream.on('data', (chunk) => {data += chunk;});    // 遇到資料讀取,将讀取到的内容指派給data

// 處理流事件 end事件
readerStream.on('end', () => {console.log(data);});    // 将讀取到的儲存到記憶體中的資料列印出來

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

console.log('end!');           

寫入流

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

// 建立一個可以寫入的流,寫入到檔案output.txt中
var writerStream = fs.createWriteStream('output.txt');

// 使用 utf8 編碼寫入資料
writerStream.write(data, 'UTF8');

// 标記檔案末尾
writerStream.end();

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

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

console.log('程式執行完畢');

           

管道流

把兩個檔案之間建立流,讓一個檔案的資料流向另外一個檔案

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

// 建立一個可讀流
var readerStream = fs.createReadStream('input.txt');

// 建立一個可寫流
var writerStream = fs.createWriteStream('output.txt');

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

console.log('程式執行完畢');
           

這裡需要注意的是,由于流的影響,導緻在操作的時候,會覆寫掉要寫入檔案的内容,原先的内容會被覆寫

鍊式流

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')    // 建立一個可讀流
    .pipe(zlib.createGzip())        // 将建立的可寫流和壓縮流連接配接
    .pipe(fs.createWriteStream('input.txt.gz'));    // 在建立可寫流,将三個流連接配接到一起

console.log('檔案壓縮完成');           

内容未被覆寫的寫入

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

具體詳細,請查文檔

https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options https://nodejs.org/api/fs.html#fs_file_system_flags

文檔寫的很詳細,後面的參數為一個對象,通過對象即可進行修改

子產品

子產品之前已經闡述過一部分,這裡闡述伺服器端的子產品

伺服器端的子產品

node中自帶了一個http子產品,在代碼中請求他,并将傳回值指派給本地變量。即本地變量變成了一個擁有所有http子產品所提供的公共方法的對象。

node中有四個子產品,(原生子產品和三種檔案子產品)

從檔案子產品的緩存中添加

原生子產品和檔案子產品優先級不同,都會優先從檔案子產品的緩存中加載以及存在的子產品

從原生子產品加載

原生子產品的優先級低于檔案子產品緩存的優先級。原生子產品也有緩存區

從檔案加載

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

繼續閱讀