结合异步迭代器实现 Node.js 流式数据复制

实现可读流到可写流数据复制,就是不断的读取->写入这个过程,那么你首先想到的是不是下面这样呢?代码看似很简单,结果却是很糟糕的,没有任何的数据积压处理。如果读取的文件很大了,造成的后果就是缓冲区数据溢出,程序会占用过多的系统内存,拖垮服务器上的其它应用,如果不明白的回顾下这篇文章 Node.js Stream 背压 — 消费端数据积压来不及处理会怎么样?

// 糟糕的示例,没有数据积压处理
readable.on('data', data => {
  writable.write(data)
});

类似以上的需求,推荐你用 pipe() 方法以流的形式完成数据的复制

作为学习,结合异步迭代器以一种简单的方式实现一个类似于 pipe 一样的方法完成数据源到目标源的数据复制。

数据写入方法实现

_write 方法目的是控制可写流的数据写入,它返回一个 Promise 对象,如果可写流的 dest.write() 方法返回 true,表示内部缓冲区未满,继续写入。

当 dest.write() 方法返回 false 表示向流中写入数据超过了它所能处理的最大能力限制,此时暂停向流中写入数据,直到 drain 事件触发,表示缓冲区中的数据已排空了可以继续写入,再将 Promise 对象变为解决。

function _write(dest, chunk) {
  return new Promise(resolve => {
    if (dest.write(chunk)) {
      return resolve(null);
    }

    dest.once('drain', resolve);
  })  
}

结合异步迭代器实现

异步迭代器使从可读流对象读取数据变得更简单,异步的读取数据并调用我们封装的 _write(chunk) 方法写入数据,如果缓冲区空间已满,这里 await _write(dest, chunk) 也会等待,当缓冲区有空间可以继续写入了,再次进行读取 -> 写入。

function myCopy(src, dest) {
  return new Promise(async (resolve, reject) => {
    dest.on('error', reject);

    try {
      for await (const chunk of src) {
        await _write(dest, chunk);
      }
      resolve();
    } catch (err) {
      reject(err);
    }
  });
}

使用如下所示:

const readable = fs.createReadStream('text.txt');
const writable = fs.createWriteStream('dest-text.txt');
await myCopy(readable, writable);

2e86911fb3276aee67bc142c6132831b.png

往期推荐

大厂面试过程复盘(微信/阿里/头条,附答案篇)

eeaf5a3adc747ab44d1ef406b0b1e920.png

面试题:说说事件循环机制(满分答案来了)

b45186b205b9914117ea8a4e55c151f9.png

专心工作只想搞钱的前端女程序员的2020

c47d76943b7084e8bf6ddf483ea26243.png


最后

  • 欢迎加我微信,拉你进技术群,长期交流学习...

  • 欢迎关注「前端Q」,认真学前端,做个专业的技术人...

44e3bb79b391a2d2f6566678b8c39f9b.png

4750fb1e14615748ea7149ec3a3738c6.png

点个在看支持我吧

8165a7dd776dcc0b956071433febb9e4.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值