55 # 实现可写流

news2024/11/24 12:49:06

先在 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, element
        if (arguments.length === 1) {
            // 在尾部添加,传入的 index 就当做是 element
            element = index;
            // 然后把 this.size 当做索引
            index = this.size;
        }
        // 处理越界可能
        if (index < 0 || index > this.size) throw new Error("越界");
        // 判断 index 是否为 0
        if (index === 0) {
            // 老的头
            let head = this.head;
            // 设置新头,将老的头变为当前节点的下一个
            this.head = new Node(element, head);
        } else {
            // 先找到当前索引的上一个
            let prevNode = this.getNode(index - 1);
            // 将当前上一个节点的 next 指向新的节点,新的节点的下一个指向上一个节点的 next
            prevNode.next = new Node(element, prevNode.next);
        }
        // 累加 size
        this.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 = () => {}) {
        // 统一转为 buffer
        chunk = 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, () => {
                // 原来用户传入的 callback
                cb();
                // 当前内容写入完毕后清空缓存区中的内容
                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.coloradmin.cn/o/770997.html

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

相关文章

数据库小白看这里,这个Oracle数据库知识图谱你值得拥有

2022年前后&#xff0c;墨天轮社区曾陆续推出PostgreSQL知识图谱、MySQL知识图谱&#xff0c;并得到了大家的广泛好评。此后&#xff0c;便有众多朋友对Oracle知识图谱发起不断“催更“。经过近期的内容搜集整合、专家复审与打磨&#xff0c;墨天轮社区正式推出Oracle知识图谱&…

MySQL五种约束类型(普通 /自增主键,外键等) + 进阶查询(聚合查询,内 /外连接查询,自连接查询,子查询,合并查询)

文章目录 前言一、五种约束NOT NULL 约束UNIQUE 约束DEFAULT 约束PRIMARY KEY 主键约束(重点)普通主键自增主键 FOREIGN KEY 外键约束(重点) 二、进阶查询聚合查询聚合函数GROUP BY子句HAVING 联合查询笛卡尔积内连接外连接自连接子查询单行子查询&#xff1a;返回一行记录的子…

AI时代图像安全“黑科技”如何助力人工智能与科技发展?

〇、前言 7月7日下午&#xff0c;2023世界人工智能大会&#xff08;WAIC&#xff09;“聚焦大模型时代AIGC新浪潮—可信AI”论坛在上海世博中心红厅举行。人工智能等技术前沿领域的著名专家与学者、投资人和领军创业者汇聚一堂&#xff0c;共同探索中国科技创新的驱动力量。 在…

搭载下一代人工智能技术,微软推出Power Automate流程挖掘产品

在近日的Microsoft Inspire大会中&#xff0c;微软揭晓了他们即将推出的Power Automate流程挖掘产品&#xff0c;并计划在8月1日正式对外开放。 试用地址&#xff1a;https://powerautomate.microsoft.com/zh-cn/#home-signup 这款产品搭载了下一代人工智能技术&#xff0c;有…

好用的思维导图软件有哪些?这几款简单好用

好用的思维导图软件有哪些&#xff1f;思维导图是一种非常有用的思维工具&#xff0c;可以帮助我们组织和理清复杂的信息。在如今的数字时代&#xff0c;有很多软件可以帮助我们创建和编辑思维导图。下面介绍几款简单好用的思维导图软件。 第一款&#xff1a;迅捷画图 这是一款…

多个信贷范围时客户主数据界面的定制(套头和信用缴纳范围=信贷范围)

客户主数据-销售范围-开票的界面有信贷范围&#xff0c;叫贷方控制范围。 但是默认是看不到的。需要进行配置。 但是SAP的配置里面的名字很奇怪&#xff0c;在客户账户组里面的销售数据中(OVT0)定制 双击后处理的这个界面&#xff0c;和界面的“”开票凭证“”对不上&#x…

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

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

Linux内核源代码的目录结构包括部分:

内核核心代码&#xff1a;这部分代码包括内核的各个子系统和模块&#xff0c;如进程管理、内存管理、文件系统、网络协议栈等。这些代码构成了Linux内核的核心功能。 非核心代码&#xff1a;除了核心代码之外&#xff0c;还包括一些非核心的代码和文件&#xff0c;如库文件、固…

【网站搭建】1安装Hexo

1.前期准备工作 安装node.js和git Node.js (nodejs.org) Git - Downloads (git-scm.com) 安装好后验证是否完成安装 2.打开Git安装配置Hexo 由于国内的镜像源速度较慢&#xff0c;所以我们利用 npm 来安装 cnpm &#xff0c;在命令行中输入npm install -g cnpm --registry…

一文详解 requests 库中 json 参数和 data 参数的用法

在requests库当中&#xff0c;requests请求方法&#xff0c;当发送post/put/delete等带有请求体的请求时&#xff0c;有json和data2个参数可选。 众所周知&#xff0c;http请求的请求体格式主要有以下4种&#xff1a; application/jsonapplicaiton/x-www-from-urlencodedmult…

291. 单词规律 II(plus题)

给你一种规律 pattern 和一个字符串 s&#xff0c;请你判断 s 是否和 pattern 的规律相匹配。 如果存在单个字符到 非空 字符串的 双射映射 &#xff0c;那么字符串 s 匹配 pattern &#xff0c;即&#xff1a;如果 pattern 中的每个字符都被它映射到的字符串替换&#xff0c;那…

python发送邮件zmail库

第三方库“zmail”和“yagmail”可实现邮件发送。在实际使用对比zmail比yagmail更简洁。使用zmail&#xff0c;无需登录OA邮箱&#xff0c;便可完成邮件的发送及附件的自动加载。 import zmaildef send_zmail(sender, sender_password, addressee, host, port465, inspect_smtp…

<C语言> 自定义类型

1.结构体 结构体是一种用户自定义的数据类型&#xff0c;允许将不同类型的数据项组合在一起&#xff0c;形成一个更大的数据结构。结构体可以包含多个成员变量&#xff0c;每个成员变量可以是不同的数据类型&#xff0c;如整数、字符、浮点数等&#xff0c;甚至可以包含其他结构…

师承AI世界新星|7天获新加坡南洋理工大学访学邀请函

能够拜师在“人工智能10大新星”名下&#xff0c;必定可以学习到前沿技术&#xff0c;受益良多&#xff0c;本案例中的C老师无疑就是这个幸运儿。我们只用了7天时间就取得了这位AI新星导师的邀请函&#xff0c;最终C老师顺利获批CSC&#xff0c;如愿出国。 C老师背景&#xff1…

线程与信号

1.子线程会继承主线程信号处理配置&#xff0c;故信号配置可以全部放在主线程内。 2.同一信号多次触发或者嵌套触发不会嵌套执行。 3.不同信号可以嵌套触发执行。 4.kill()触发的信号由进程&#xff08;主线程&#xff09;执行&#xff0c;pthread_kill()触发的信号由参数指…

数据结构-单链表

#include<stdio.h> #include<stdlib.h>typedef struct Node {int data;struct Node* next; }Node;//创建一个头结点&#xff0c;数据域保存链表节点数 Node* init_single_list() {Node* node (Node*)malloc(sizeof(Node));node->next NULL;node->data 0; …

想知道搭建知识库有什么重点?看这篇就够了

在目前这个提倡无纸化的时代&#xff0c;搭建一个知识库已经是一种潮流。无论是个人还是企业来说&#xff0c;都是特别重要的一个工具。今天looklook就从搭建知识库的重点这方面来展开&#xff0c;详细地告诉大家该如何成功搭建一个完善的知识库。 搭建知识库的重点 1.建立素材…

ASUS华硕飞行堡垒8笔记本FX506LH LI LU FX706原装出厂 Win10系统工厂模式20H2

自带所有驱动、出厂主题壁纸LOGO、Office办公软件、MyASUS电脑管家、奥创控制中心等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;HDI,SWP,OFS,EDN,KIT,TLK多个底包 文件大小&#xff1a;10.95GB 注&#xff1a;恢复时会清空电脑上所有盘的数据&…

大小端模式

文章目录 一、概念二、举例三、判大小端和交换 一、概念 大端模式&#xff08;Big-endian&#xff09;&#xff0c;是一种数据存储方式&#xff0c;其中较高的字节&#xff08;最高有效字节&#xff09;存储在较低的内存地址&#xff0c;较低的字节&#xff08;最低有效字节&am…

php 开发微信 h5 支付 APIv3 接入超详细流程

✨ 目录 &#x1f388; 申请商户号&#x1f388; 申请商户证书&#x1f388; 设置V3密钥&#x1f388; 开通H5支付&#x1f388; 设置支付域名&#x1f388; SDK 下载&#x1f388; 第一次下载平台证书&#x1f388;非第一次下载平台证书&#x1f388; H5下单 &#x1f388; 申…