55 # 实现可写流

先在 LinkedList.js 给链表添加一个移除方法

class Node {constructor(element, next) {this.element = element;this.next = next;}
}class LinkedList {constructor() {this.head = null; // 链表的头this.size = 0; // 链表长度}// 可以直接在尾部添加内容,或者根据索引添加add(index, element) {// 传入一个参数是需要设置一下 index, elementif (arguments.length === 1) {// 在尾部添加,传入的 index 就当做是 elementelement = index;// 然后把 this.size 当做索引index = this.size;}// 处理越界可能if (index < 0 || index > this.size) throw new Error("越界");// 判断 index 是否为 0if (index === 0) {// 老的头let head = this.head;// 设置新头,将老的头变为当前节点的下一个this.head = new Node(element, head);} else {// 先找到当前索引的上一个let prevNode = this.getNode(index - 1);// 将当前上一个节点的 next 指向新的节点,新的节点的下一个指向上一个节点的 nextprevNode.next = new Node(element, prevNode.next);}// 累加 sizethis.size++;}getNode(index) {// 从头开始找let current = this.head;// 不能向后找,找到索引的位置for (let i = 0; i < index; i++) {current = current.next;}return current;}remove(index) {if (index === 0) {let node = this.head;if (!node) return null;this.head = node.next;this.size--;return node.element;}}
}let ll = new LinkedList();
ll.add(0, 1);
ll.add(0, 2);
ll.add(3);
ll.add(1, 4);console.dir(ll, { depth: 100 });
console.dir(ll.remove(0));
console.dir(ll, { depth: 100 });module.exports = LinkedList;

在这里插入图片描述

下面实现可写流:

  1. 先创建一个队列的类,利用上面 LinkedList 维护一个链表
  2. 然后创建自己的可写流 KaimoWriteStream 类继承 EventEmitter
  3. 再区分是否是在写入状态,根据写入状态确定存缓存还是真正的写入
  4. 最后写入完一个之后,判断是否需要清空缓存,需要的话就继续将 poll 返回的数据继续写入
const EventEmitter = require("events");
const fs = require("fs");
let LinkedList = require("./LinkedList");class Queue {constructor() {this.LinkedList = new LinkedList();}offer(element) {this.LinkedList.add(element);}poll() {return this.LinkedList.remove(0);}
}class KaimoWriteStream extends EventEmitter {constructor(path, opts = {}) {super();this.path = path;this.flags = opts.flags || "w";this.autoClose = opts.autoClose || true;this.encoding = opts.encoding || "utf8";this.start = opts.start || 0;this.mode = opts.mode || 0o666;this.highWaterMark = opts.highWaterMark || 16 * 1024;// 维护当前存入的数据个数// 每次调用 write 方法,会根据写入的内容的个数累加给 len 属性(缓存的长度)this.len = 0;// 是否正在写入this.writing = false;// 是否需要触发 drain 事件this.needDrain = false;// 写入的偏移量this.offset = this.start;// 用来缓存的队列this.cache = new Queue();// 默认先打开文件this.open();}// open 方法是异步的open() {fs.open(this.path, this.flags, this.mode, (err, fd) => {if (err) {return this.emit("error", err);}// 将 fd 保存到实例上,用于稍后的读取操作this.fd = fd;this.emit("open", fd);});}write(chunk, encoding = "utf8", cb = () => {}) {// 统一转为 bufferchunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);this.len += chunk.length;// write 方法的返回值let flag = this.len < this.highWaterMark;// drain 事件的触发:1.必须写入的个数达到预期或者超过预期this.needDrain = !flag;if (this.writing) {// 正在写入this.cache.offer({chunk,encoding,cb});} else {// 没有正在写入this.writing = true; // 标识正在写入了// 真正写入的逻辑this._write(chunk, encoding, () => {// 原来用户传入的 callbackcb();// 当前内容写入完毕后清空缓存区中的内容this.clearBuffer();});}return flag;}_write(chunk, encoding, cb) {// 写入必须要等待文件打开完毕,如果打开了会触发 open 事件if (typeof this.fd !== "number") {// 如果没有 fd 就返回一个 open 的一次性事件,再去回调 _write 方法return this.once("open", () => this._write(chunk, encoding, cb));}// 将用户数据写入到文件中fs.write(this.fd, chunk, 0, chunk.length, this.offset, (err, written) => {if (err) {return this.emit("error", err);}this.len -= written; // 缓存中的数量要减少this.offset += written;console.log("chunk--->", chunk.toString());cb(); // 当前文件内容写入完毕后,再去清空缓存中的});}clearBuffer() {let data = this.cache.poll();if (data) {// 需要清空缓存let { chunk, encoding, cb } = data;this._write(chunk, encoding, () => {cb();// 当前缓存的第一个执行后,再去清空第二个this.clearBuffer();});} else {this.writing = false;if (this.needDrain) {// 当前触发后下次就不需要再次触发了this.needDrain = false;this.emit("drain");}}}
}module.exports = KaimoWriteStream;

下面用实现的可写流测试一下上一节的例子:写入10个数,只占用一个字节的内存

const path = require("path");const KaimoWriteStream = require("./55/KaimoWriteStream");let ws = new KaimoWriteStream(path.resolve(__dirname, "./55/number.txt"), {highWaterMark: 3 // 利用 highWaterMark 来控制写入的速率
});let numberIndex = 0;
function write() {let flag = true; // 是否可以写入while (flag && numberIndex < 10) {flag = ws.write(numberIndex + "");numberIndex++;}
}
write();
ws.on("drain", () => {console.log("ws---drain--->");write();
});

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/29774.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国内流行的数据可视化软件工具

在信息爆炸的时代&#xff0c;越来越多的数据堆积如山。但是&#xff0c;这些密集的数据没有重点且可读性较差。因此&#xff0c;我们需要数据可视化来帮助数据易于理解和接受。相比之下&#xff0c;可视化更直观、更有意义&#xff0c;使用适当的数据可视化工具来可视化数据非…

密码学总结杂七杂八的wp

快捷键 折叠&#xff08;展开&#xff09;所有代码这里是指按下快捷键后凡事.py文件里可折叠的都折叠。 折叠所有代码&#xff1a;Ctrl Shift - &#xff08;减号&#xff09; 展开所有代码&#xff1a;Ctrl Shift &#xff08;加号&#xff09; 折叠&#xff08;展开&…

在Illustrator中创建 3D 冰淇淋模型对象

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 一旦你学会了如何在Illustrator中制作一个对象3D&#xff0c;你可以前往Envato Elements&#xff0c;在那里你可以找到大量的3D设计来激发你的灵感。这个基于订阅的市场拥有超过 2&#xff0c;000 个 Illus…

一、音频基础-音频分析的重要工具(语谱图)

文章目录 1. 傅里叶转换2. 语谱图3. 应用1. 傅里叶转换 通过前面的描述可以知道,声音的本质就是各种声波,那么任意某一个时刻,都不可能是只有一个频率的波,而且声波也不可能是我们理解的标准的正弦波: 而一般我们对声音进行处理时,需要分析出频率当中的有哪些频率,然…

麒麟SP3X86系统下,安装Oracle11g数据库

目录 1、写在前面 2、准备工作 2.1 环境准备 2.2 数据库安装前准备 2.2.1 安装依赖 2.2.2 系统环境准备 2.2.3 上传软件安装包 2.2.4 安装调图形化界面的依赖和相关设置 3、执行安装程序 1、写在前面 随着国产化进程&#xff0c;各大应用需要在国产服务器上面进行部署…

Python调用ImageMagick生成PDF文件缩略图

使用Python调用ImageMagick生成PDF文件缩略图 Imagemagick使用Ghostscript作为其依赖项之一&#xff0c;以便能够处理和转换PDF相关的图像。 准备 安装Ghostscript&#xff0c;网站安装ImageMagick&#xff0c;网站 安装完毕后&#xff0c;需要自行配置环境路径 脚本 使用示…

准备WebUI自动化测试面试?这30个问题你必须掌握(二)

本文共有11000字&#xff0c;包含了后十五个问题&#xff0c;如需要前十五个问题&#xff0c;可查看文末链接~ 16. 在WebUI自动化测试中&#xff0c;你如何处理验证码或图像识别的问题&#xff1f; 1. 人工识别&#xff1a;一种简单但费时费力的方法是使用人工手动识别验证码。…

pycharm里debug时torch数组显示不全

pycharm里查看torch数组全部值 一、在Pycharm运行torch数组时&#xff0c;通常只能看到数组的一部分二、解决办法1、debug后&#xff0c;鼠标右键想要查看完整的数组&#xff0c;选择Evaluate Expression2、输入np.array(x0.data)&#xff0c;x0为想要查看的数组名&#xff0c;…

Kubernetes Volume及其类型(NFS、SAN) - PV - PVC - PV与PVC与Pod的关系

目录 volume 卷 官方文档&#xff1a;卷 | Kubernetes 一、emptyDir&#xff08;临时卷&#xff09; 二、hostPath卷 type字段参数 hostPath 实验&#xff1a; 三、第3方提供的存储卷&#xff08;百度云、阿里云、亚马逊云、谷歌云等&#xff09; 四、local卷 五、NF…

mysql、redis 、RabbitMQ只能本机访问,怎么改?

如果只能本机访问&#xff0c;怎么改? 一、mysql - 改my.ini 刷脚本 bind-address0.0.0.0 然后重启一下mysql服务 任务管理器-关掉mysql 搜索 计算机管理-重启mysql服务 然后 打开查询&#xff0c;并选择mysql数据&#xff0c;输入这个sql语句&#xff0c;点击运行 sele…

解码 LangChain|用 LangChain 和 Milvus 从零搭建 LLM 应用

如何从零搭建一个 LLM 应用&#xff1f;不妨试试 LangChain Milvus 的组合拳。 作为开发 LLM 应用的框架&#xff0c;LangChain 内部不仅包含诸多模块&#xff0c;而且支持外部集成&#xff1b;Milvus 同样可以支持诸多 LLM 集成&#xff0c;二者结合除了可以轻松搭建一个 LLM…

Python 算法基础篇之数组和列表:创建、访问、添加和删除元素

Python 算法基础篇之数组和列表&#xff1a;创建、访问、添加和删除元素 引用 1. 数组的概念和创建2. 列表的概念和创建3. 访问数组和列表中的元素4. 添加和删除元素 a ) 添加元素 b ) 删除元素 总结 引用 在算法和数据结构中&#xff0c;数组和列表是常见的数据结构&#xff…