events模块
先介绍一下基本的API
// 将events模块加载进来
let eventEmitter = require('events');
// 这是一个工具箱模块,有兴趣的自行学习一下,这边只用到一个继承
let util = require('util');
function A() {}; // 创建一个类A
util.inherits(A, eventEmitter) // 让A继承eventEmitter公有方法
let a = new A;
a.on('eventName', (e) => {}); // 订阅一个事件,名字就叫eventName
a.on('eventName', (e) => {}); // 再绑定一个
a.emit('eventName', e)
// 触发eventName订阅的事件,
// 并将参数e作为参数传入.如果事件有监听器就返回一个true,否则返回false
a.once('eventName', () => {})
// once和on比较,就是只能绑定一个监听器,之后的绑定都是无效的
a.setMaxListeners(3) // 设置可以绑定监听器的数量 最大监听器限制值 默认是10个
a.prependListener('eventName', () => {})
// 绑定一个监听器在所有监听器的最前面执行,就是一个执行顺序的问题
a.prependOnceListener('eventName', () => {})
//和once一样,只绑定一次
a.removeListener('eventName', callback); // 移除一个指定的监听器
a.removeAllListeners('eventName') // 移除所有监听器
复制代码
上面是events模块主要的API,所有的流都是eventEmitter的实例
let eventEmitter = require('events');
let util = require('util');
function A() {}
util.inherits(A, eventEmitter);
let a = new A;
a.on('test', (e) => {
console.log(e, 'test1');
})
a.on('test', (e) => {
console.log(e, 'test2');
})
a.prependOnceListener('test', (e) => {
console.log(e, 'test3')
})
console.log(a.emit('test', 1, 2, 3))
// 执行结果顺序是
// 1 'test3'
// 1 'test1'
// 1 'test2'
// true
复制代码
可读流与可写流
可读流
let fs = require('fs');
let rs = fs.createReadStream(path, {
flags, // 默认 'r'
encoding, // 默认是null null代表的是buffer
autoClose, // 默认true, 读取完自动关闭
highWaterMark, // 默认是64k 64*1024b
start, // 开始读取位置包括
end, // 结束读取位置包括
})
rs.on('open', () => {}) // 文件打开时触发
rs.on('close', () => {}) // 文件关闭时触发
rs.on('error', () => {}) // 报错时触发
rs.on('data',function(data){
//每读highWaterMark量的数据,就会触发一次,也就是说可能触发多次
console.log(data);
rs.pause(); // 暂停方法 表示暂停读取,暂停data事件触发
rs.resume() // 继续读取
});
rs.on('end', () => {}); // 流结束时触发
文件 q.txt
123456
let fs = require('fs');
let path = require('path')
let rs = fs.createReadStream(path.join(__dirname, 'q.txt'), {
highWaterMark: 2,
// encoding: 'utf8'
})
rs.on('data', (data) => {
console.log(data)
})
// 触发了三次输出结果就是
// <Buffer 31 32>
// <Buffer 33 34>
// <Buffer 35 36>
// 如果设置 encoding: 'utf8' 结果就是
// 12
// 34
// 56
复制代码
可写流
let fs = require('fs');
let ws = fs.createWriteStream(path, {
flags, // 'w',
autoClose, // true,
highWaterMark, // 默认写是16k
encoding, // 默认 null 就是buffer意思
})
let flag = ws.write(data, 'utf8', () => {})
// flag是布尔值表示是否可以继续写入
ws.on('drain', () => {})
// 缓存区满了,再被清空后出发drain
let ws = fs.createWriteStream('q.txt', {
highWaterMark: 3
})
let i = 6
function write() {
let flag = true;
while(i && flag) {
console.log('i', i);
flag = ws.write(i-- + '', () => { console.log('ok') })
}
}
write()
ws.on('drain', () => {
console.log('drain')
write()
})
// 输出结果, 因为是异步,可以不看'ok'
// 因为 'highWaterMark' 设置的是 3
// 所以,ws.write运行到第三次返回的就是 false
i 6
i 5
i 4
ok
drain
i 3
i 2
i 1
ok
ok
ok
drain
ok
ok
复制代码
简单实现可读流源码
let EventEmitter = require('events');
let fs = require('fs');
class ReadStream extends EventEmitter {
constructor(path, options) {
super();
this.path = path;
this.flags = options.flags || 'r';
this.autoClose = options.autoClose || true;
this.highWaterMark = options.highWaterMark || 64*1024;
this.start = options.start || 0;
this.end = options.end;
this.encoding = options.encoding || null;
this.open(); // 打开文件 fd
this.flowing = null; // null就是暂停模式
// 看是否坚挺了打他时间,如果监听了 就变成流动模式
// 建立一个buffer 这个buffer就是要一次读多少
this.buffer = Buffer.alloc(this.highWaterMark);
this.pos = this.start; // 开始读取的位置
this.on('newListener', (eventName, callback) => {
if (eventName === 'data') {
// 相当于用户监听了data事件
this.flowing = true;
// 监听了 就去读
this.read();
}
})
}
read() {
// 此时文件还没打开呢
if(typeof this.fd !== 'number') {
return this.once('open', () => {this.read()})
}
let howMuchToRead = this.end ? Math.min(this.highWaterMark, this.end - this.pos + 1) : this.highWaterMark;
fs.read(this.fd, this.buffer, 0, howMuchToRead, this.pos, (err, bytesRead) => {
if (bytesRead > 0) {
this.pos += bytesRead;
let data = this.encoding ? this.buffer.slice(0, bytesRead).toString(this.encoding) : this.buffer.slice(0, bytesRead);
this.emit('data', data);
// 当读取的位置 大于了末尾 就是读取完毕了
if (this.pos > this.end) {
this.emit('end');
this.destroy();
}
if (this.flowing) {
this.read();
}
} else {
this.emit('end');
this.destroy();
}
})
}
pipe(ws) {
this.on('data', (chunk) => {
let flag = ws.write(chunk);
if (!flag) {
this.pause();
}
})
ws.on('drain', () => {
this.resume();
})
}
resume() {
this.flowing = true;
this.read();
}
pause() {
this.flowing = false;
}
destroy() {
// 先判断有没有fd 有关闭文件 触发close事件
if(typeof this.fd === 'number') {
fs.close(this.fd, () => {
this.emit('close');
})
return
}
this.emit('close')
}
open() {
// 先打开文件
fs.open(this.path, this.flags, (err, fd) => {
if(err) {
this.emit('error', err);
if(this.autoClose) {
this.destroy();
}
return;
}
this.fd = fd; // 记录文件描述符
this.emit('open'); // 文件打开了
})
}
}
module.exports = ReadStream;
复制代码
简单实现可写流源码
let EventEmitter = require('events');
let fs = require('fs');
class WriteStream extends EventEmitter{
constructor(path, options) {
super();
this.path = path;
this.highWaterMark = this.highWaterMark || 16*1024;
this.autoClose = this.autoClose || true;
this.mode = options.mode;
this.start = options.start || 0;
this.flags = options.flags || 'w';
this.encoding = options.encoding || 'utf8';
// 可写流 要有一个缓存区,当正在写入文件时,内容需要写入到缓存中
// 在源码中是一个链表 => []
this.buffers = [];
// 标识 是否正在写入
this.writing = false;
// 是否满足触发drain事件
this.needDrain = false;
// 记录写入的位置
this.pos = 0;
// 记录缓存区的大小
this.length = 0;
this.open();
}
destroy() {
if(typeof this.fd !== 'number') {
return this.emit('close');
}
fs.close(this.fd, () => {
this.emit('close')
})
}
open() {
fs.open(this.path, this.flags, this.made, (err, fd) => {
if(err) {
this.emit('error', err);
if(this.autoClose) {
this.destroy();
}
return
}
this.fd = fd;
this.emit('open');
})
}
write(chunk, encoding = this.encoding, callback = () => {}){
chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding);
// write 返回一个boolean类型
this.length += chunk.length;
let ret = this.length < this.highWaterMark; // 比较是否达到了缓存区的大小
this.needDrain = !ret; // 是否需要触发drain
// 判断是否正在写入 如果是正在写入 就写到缓存区中
if(this.writing) {
this.buffers.push({
encoding,
chunk,
callback
});
} else {
// 专门用来将内容 写入到文件内
this.writing = true;
this._write(chunk, encoding, () => {
callback();
this.clearBuffer();
})
}
return ret;
}
clearBuffer() {
let buffer = this.buffers.shift();
if(buffer){
this._write(buffer.chunk, buffer.encoding, () => {
buffer.callback();
this.clearBuffer();
})
} else {
this.writing = false;
if (this.needDrain) {
this.needDrain = false;
this.emit('drain');
}
}
}
_write(chunk, encoding, callback) {
if(typeof this.fd !== 'number') {
return this.once('open', () => this._write(chunk, encoding, callback));
}
fs.write(this.fd, chunk, 0, chunk.length, this.pos, (err, byteWritten) => {
this.length -= byteWritten;
this.pos += byteWritten;
callback(); // 清空缓存区的内容
})
}
}
module.exports = WriteStream;
复制代码
有错误的地方还请指正