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.mzph.cn/news/1675.shtml

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

相关文章

聊聊ChatGPT是如何组织对话的

为什么要组织对话&#xff1f; 总所周知&#xff0c;ChatGPT的训练大致可分为下图中展示的几个阶段&#xff0c;其中&#xff0c;在Pretraining阶段&#xff0c;模型的训练数据是纯文本&#xff0c;目标是根据上文预测下一个token&#xff0c;而在后面的几个阶段中&#xff0c…

网络安全能力成熟度模型介绍

一、概述 经过多年网络安全工作&#xff0c;一直缺乏网络安全的整体视角&#xff0c;网络安全的全貌到底是什么&#xff0c;一直挺迷惑的。目前网络安全的分类和厂家非常多&#xff0c;而且每年还会冒出来不少新的产品。但这些产品感觉还是像盲人摸象&#xff0c;只看到网络安…

回归预测 | MATLAB实现WOA-CNN鲸鱼算法优化卷积神经网络的数据多输入单输出回归预测

回归预测 | MATLAB实现WOA-CNN鲸鱼算法优化卷积神经网络的数据多输入单输出回归预测 目录 回归预测 | MATLAB实现WOA-CNN鲸鱼算法优化卷积神经网络的数据多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 回归预测 | MATLAB实现WOA-CNN鲸鱼算法优化卷积…

数据采集专家----4通道AD采集子卡推荐

FMC136是一款4通道250MHz采样率16位AD采集FMC子卡&#xff0c;符合VITA57规范&#xff0c;可以作为一个理想的IO模块耦合至FPGA前端&#xff0c;4通道AD通过高带宽的FMC连接器&#xff08;HPC&#xff09;连接至FPGA从而大大降低了系统信号延迟。 该板卡支持板上可编程采样时钟…

Linux进程

Linux进程 对于进程的理解&#xff0c;我们要从计算机的重要的冯诺依曼体系结构讲起&#xff0c;只有知道我们的程序/文件是如何在计算机中被操作运行并输出到显示器中&#xff0c;通过对于操作系统的理解&#xff0c;才能对于进程进行一定的理解。 文章目录 Linux进程冯诺依…

c#示例-json序列化和json树

序列化 由于指针和引用类型的存在&#xff0c;在运行中的程序中&#xff0c;数据不一定是整块的。 可能东一块西一块散落在内存的各个地方。 序列&#xff0c;是指连续且有序的一个整体。序列化就是把数据变为连续有序整体的过程。 经过这样处理后的数据就可以方便的进行传输…

数据结构--时间复杂度与空间复杂度

数据结构–时间复杂度与空间复杂度 文章目录 数据结构--时间复杂度与空间复杂度时间复杂度一、什么是时间复杂度二、具体实例1.大O的渐进表示法2.二分查找的时间复杂度 空间复杂度一、什么是空间复杂度二、具体实例总结 时间复杂度 一、什么是时间复杂度 在计算机科学中&…

云原生微服务应用的平台工程实践

作者&#xff1a;纳海 01 微服务应用云原生化 微服务是一个广泛使用的应用架构&#xff0c;而如何使得微服务应用云原生化却是近些年一直在演进的课题。国内外云厂商对云原生概念的诠释大同小异&#xff0c;基本都会遵循 CNCF 基金会的定义&#xff1a; 云原生技术有利于各组…

【后端面经-Java】JVM垃圾回收机制

【后端面经-Java】JVM垃圾回收机制 1. Where&#xff1a;回收哪里的东西&#xff1f;——JVM内存分配2. Which&#xff1a;内存对象中谁会被回收&#xff1f;——GC分代思想2.1 年轻代/老年代/永久代2.2 内存细分 3. When&#xff1a;什么时候回收垃圾&#xff1f;——GC触发条…

【MySQL】根据MVCC和Read View分析事务的四种隔离级别在读写场景分别是如何体现其隔离性的

目录 一、数据库并发的三种场景 二、读写场景的MVCC 1、3个&#xff08;4个&#xff09;记录隐藏列字段 2、undo log&#xff08;撤销日志&#xff09; 3、模拟MVCC场景 3.1update场景 3.2delete场景 3.3insert 3.4select场景 4、Read View 5、RR和RC的区别 5.1当…

Windows安装激活注意事项

选择语言、版本&#xff08;Windows 10指的是专业版本&#xff09;和体系结构&#xff08;32位/64位&#xff09;&#xff0c;这里自行根据情况选择&#xff08;如果机器预装的是Windows 10家庭中文版则选择家庭中文版&#xff0c;如果预装的是专业版则选择Windows 10。这样原先…

Revit 导出明细表的两种方法!

方法一、Revit中怎么灵活运用明细表格式的导出与导入 在做项目的时候&#xff0c;遇到一些项目需要进行工程量统计的时候&#xff0c;经常需要设置明细表里面的格式&#xff0c;例如字体、表格排布样式等&#xff0c;但是项目一旦多起来&#xff0c;这些工作重复性又太高&#…

适合小公司的自动化部署脚本

背景&#xff08;偷懒&#xff09; 在小小的公司里面&#xff0c;挖呀挖呀挖。快挖不动了&#xff0c;一件事重复个5次&#xff0c;还在人肉手工&#xff0c;身体和心理就开始不舒服了&#xff0c;并且违背了个人的座右铭&#xff1a;“偷懒”是人类进步的第一推动力。 每次想…

解决MAC IDEA终端每次都要source ~/.zshrc

安装nvm之后&#xff0c;发现每隔一段时间&#xff08;不清楚是新打开一个终端还是会定时刷新&#xff09;就要重新执行source ~/zshrc&#xff0c;才能执行nvm命令。找了一圈发现idea默认使用的shell是bash&#xff0c;将默认的shell改成zsh就可以&#xff0c;更改位置&#x…

【运维】shell监控脚本结合钉钉机器人实现服务及服务器监控告警

文章目录 前言一、监控shell脚本和钉钉机器人二、创建钉钉机器人&#xff1a;1.在钉钉群聊里点击设置2.在设置里点击机器人选项3.再点击添加机器人4.再点击选择自定义机器人5.设置机器人名称、是否加密、是否限制ip、以及触发关键字6.获取机器人的Webhook地址 三、编写监控脚本…

[爬虫]解决机票网站文本混淆问题-实战讲解

前言 最近有遇到很多小伙伴私信向我求助&#xff0c;遇到的问题基本上都是关于文本混淆或者是字体反爬的问题。今天给大家带来其中一个小伙伴的实际案例给大家讲讲解决方法 &#x1f4dd;个人主页→数据挖掘博主ZTLJQ的主页 ​​ 个人推荐python学习系列&#xff1a; ☄️爬虫J…

架构训练营3:架构设计流程和架构师职责

架构师相关职责&#xff1a; 架构师是业务和技术之间的桥梁&#xff0c;架构师不能只顾技术&#xff0c;不懂业务&#xff0c;架构师很容易两头不讨好 三个核心能力&#xff1a; 判断&#xff1a;1业务理解力2.技术能力3.沟通能力 拆解&#xff1a;1技术深度2.技术宽度3.技术…

基于单片机指纹考勤系统的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;利用指纹采集模块存储打卡信息&#xff1b;12864显示当前考勤信息&#xff0c;时间 &#xff1b;如果迟到 语音播报 您已迟到&#xff1b;按键进行注册指纹、删除指纹、设置当前时间和签到时间、查询打卡等&#xff1b;具有掉电保存…

利用Python和Selenium编程,实现定时自动检索特定网页,发现特定网页内容发生变化后,向管理员发送提醒邮件(一)

一、项目需求 要求爬取某单位网站&#xff0c;登录后台查看是否有新增“网友提问”&#xff0c;如果有新的提问&#xff0c;向特定邮箱发出提醒邮件。 二、项目分析 &#xff08;一&#xff09;判断是否可用爬虫爬取相关内容 首先查看该网站的robots.txt文件&#xff0c;发现…

JAVA开发(记一次504 gateway timeout错误排查过程)

一、问题与背景&#xff1a; 最近在发布一个web项目&#xff0c;在测试环境都是可以的&#xff0c;发布到生产环境通过IP访问也是可以的&#xff0c;但是通过域名访问就出现504 gateway timeout。通过postman去测试接口也是一样。ip和端口都可以通&#xff0c;域名却不行&…