Stream API
转换流 API
一个转换流接收所有的分块将其转换为 Uint8Array,上面可读流中的 value 也为该 Uint8Array 格式
TransformStream
TransformStream 是 Web Streams API 的一部分,它允许你通过提供一个转换函数来创建一个可写和可读的流对 → 这个转换函数接收来自上游的数据块(chunks),执行某些操作(如转换、过滤等),然后将结果数据块发送到下游
→ 链式管道传输(pipe chain)转换流概念的具体实现
主要用途:
转换数据:
在数据从源流向目的地的过程中对数据进行处理
保持背压:
当下游消费数据的速度跟不上上游生产数据的速度时,能够自动暂停上游的写入操作,直到下游准备好继续接收数据
TransformStream 转换流实例对象的创建: new TransformStream(transformer, writableStrategy, readableStrategy)
-
transformer(可选):
该配置对象下面的每一个方法中都包含一个 controller(TransformStreamDefaultController 实例对象)参数
-
start(controller):
该方法会在 TransformStream 实例对象被构建时来调用
通常用于 TransformStreamDefaultController.equeue 将块压入流的队列中
-
transform(chunk,controller):
-
当一个数据块(chunk)被写入到 WritableStream 端时,transform 函数会被调用,我们可以在该函数中对 chunk 做一些转换(一些自定义操作,开发者决定)后在通过 controller 中的 enqueue 压入队列
-
即我们可以理解为一个 chunk 在被写入可写流之前,我们可以在该方法中先对 chunk 进行一些自定义的操作(转换),在将 chunk 压入到对应流的队列中(也可以理解为是一个拦截器)
-
{ // -- 如 ↓: 当一个 chunk 被写入到可写流时,就会调用该函数,我们可以该函数对 chunk 进行一些转换等操作,如下示例转换成大小字符 transform(chunk, controller) { controller.enqueue(chunk.toUpperCase()); } } // 这个转换流可以被用于管道操作中,如将文本数据从一种格式转换为另一种格式
-
-
flush(controller):
当所有数据块都已经被写入到 WritableStream 端时,调用该方法且会关闭可写流
可以理解为当已将所有数据写入后,调用了 writer 中的 close 关闭可写流的写入时触发该函数
-
-
writableStrategy(可选):
可写流的策略配置对象,具体参数与上面可读流一样,略...
-
readableStrategy(可选):
可读流的策略配置对象,具体参数与上面可读流一样,略...
实例属性: 转换流实例对象上只有两个属性,分别对应 ReadableStream 和 WritableStream 实例对象
readable:
返回当前转换流中的 ReadableStream 可读流实例对象
writable:
返回当前转换流中的 WritableStream 可写流实例对象
基本使用示例:
-
const transfer = new TransformStream({ // -- 1. 创建一个转换流 start(controller) { console.log("init transform stream") }, transform(chunk, controller) { // -- 2. 当一个 chunk 即将被写入到可写流中时,就会触发该方法 → 我们可以在 chunk 写入前在该方法中对 chunk 进行一些转换等操作 → 如下示例 // -- 如下: 这里先将 chunk 转换成大写(可写流需写入字符),转换成大写后再通过 controller.enqueue 将其压入对应的队列中(使用该新的 chunk 进行写入可写流中) const chunkTransformed = chunk.toUpperCase() controller.enqueue(chunkTransformed) }, flush(controller) { // -- 3. 当所有数据块都写入完时(即调用 writer.close() 关闭可写流时),触发该方法 → 可在该方法中做一些清除操作等 console.log("flush") controller.terminate() // -- 当流已经全部处理完时,通过该方法来结束流的处理过程(关闭流) } })
-
// -- 4. 通过转换流 TransformStream 实例对象上的 readable 和 writer 属性获取对应的可读流与可写流对象 const readable = transfer.readable const writable = transfer.writable // -- 5. 通过 readable 和 writable,获取对应的读取器 reader 与写入器 writer → 当然也可以直接通直接获取(如: transfer.readable.getReader),这里为了每一步都清除一些,所以就逐步来获取 const reader = readable.getReader() const writer = writable.getWriter()
-
// -- 6. 当 desiredSize 为正数时(无需背压),在里面模拟服务服务器返回流数据进行写入 writer.ready.then(() => { writer.write("Kong") // -- 通过 setTimeout 默认流数据的请求 setTimeout(() => writer.write("Xiang"), 200) setTimeout(() => writer.write("Hunag"), 400) setTimeout(() => writer.write("Xiao"), 600) setTimeout(() => { writer.write("Kong") writer.close() // -- 当所有数据都写入完成后,关闭可写流(如果不关闭,可读流在读取是对应的 done 属性将一直为 false,同理也不会触发上面转换流中的 flush 方法) }, 800) }).catch(err => { console.log("写入失败"); })
-
// -- 7. 通过 reader 读取流中所有的数据块 const read = () => { reader.read().then(({ value, done }) => { // -- 读取流中的每一个数据块 if (done) { // -- 当流中所有数据块都读取完毕后,设置提示并返回(反之,在下面递归读取数据块) console.log("Stream readed completed") return } console.log(value) // -- 打印当前数据块内容 read() // -- 递归读取 }) } read() // -- 开始读取
TransformStreamDefaultController
该 API 用于提供对应操作 ReadableStream 和 WritableStream 的方法 → 该方法同样是没有对应的构造函数的,当在构造 TransformStream 实例对象时,会自动创建对应的 TransformStreamDefaultController 实例对象(传递给对应 TransformStream 的行为方法中)
实例属性:
desired:
属性返回填充满流内部队列的可读端所需要的大小
实例方法:
-
enqueue:
该方法用于将给定的分块排入流的可读端
→ enqueue(chunk)
-
error:
该方法可以向流的两端(可读流和可写流)抛出错误 → 与它的进一步交互都会失败并携带给定的错误信息,并且队列中的任何分块都将被丢弃
→ error(reason)
-
// -- 在这个示例中,当一个分块中包含 symbol 时,error() 方法被使用 → 由开发者控制 case 'symbol': controller.error("Cannot send a symbol as a chunk part") break
-
-
terminate:
该方法用于关闭两端的流(可读流和可写流)
→ terminate()
-
场景: → 正常完成:当转换器处理完所有输入数据,并且没有更多的数据需要发送到下游时 → 错误处理:如果在转换过程中遇到无法恢复的错误,并且你希望立即停止流的进一步处理 → 条件终止:基于特定的输入或状态条件,你可能决定提前终止流
-
该 API 具体的用法,在 TransformStream 中进行示例