【问题标题】:Using Web Streams, create a TransformStream from several TransformStreams使用 Web Streams,从多个 TransformStreams 创建一个 TransformStream
【发布时间】:2023-01-11 05:10:10
【问题描述】:

是否可以使用 whatwg 流(网络 Streams API)从其他几个 TransformStreams 中创建一个 TransformStream

例如,如果我有两个按顺序运行的 TransformStreams,如 transformer1transformer2

readable.pipeThrough(transformer1).pipeThrough(transformer2).pipeTo(writable)

最终,我希望能够将其转换为

readable.pipeThrough(allTransformers).pipeTo(writable)

其中 allTransformers 是组合 transformer1transformer2 的 TransformStream。

下面不是真正的功能代码,但我认为会有一种方法可以做这样的事情:

const allTransformers = transformer1.pipeThrough(transformer2)

这显然是一个简化的例子,但你可以想象有许多转换流,我想重构为一个单一的、可重用的转换管道。

【问题讨论】:

    标签: javascript whatwg-streams-api


    【解决方案1】:

    我有同样的问题,这是我的解决方案。

    在下面的示例中,UpperCaseReadableStream 通过TextDecoderStreamUpperCaseTextStreamTextEncoderStream 传输流。

    class UpperCaseTextStream extends TransformStream {
      constructor() {
        super({
          transform(chunk, controller) {
            controller.enqueue(chunk.toUpperCase());
          }
        });
      }
    }
    
    class UpperCaseReadableStream {
      constructor(...strategies) {
        const { writable, readable } = new TransformStream({}, ...strategies);
        this.writable = writable;
        this.readable = readable
          .pipeThrough(new TextDecoderStream())
          .pipeThrough(new UpperCaseTextStream())
          .pipeThrough(new TextEncoderStream());
      }
    }
    
    const TEXT_CHUNK =
     "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy " +
     "nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.";
    const TEXT_CONTENT = new Array(1024 * 1024).fill(TEXT_CHUNK).join("");
    const readableInput = new Blob([TEXT_CONTENT]).stream();
    const readableOuput = readableInput.pipeThrough(new UpperCaseReadableStream());  
    (async () => {
      const text = await new Response(readableOuput).text();
      console.log("Test OK?", text == TEXT_CONTENT.toUpperCase());
    })().catch(console.error);

    这是使用类 PipelineStream 的更通用的实现,它扩展 TransformStream 并接受 TransformStream 实例数组作为第一个参数。

    class UpperCaseTextStream extends TransformStream {
      constructor() {
        super({
          transform(chunk, controller) {
            controller.enqueue(chunk.toUpperCase());
          }
        });
      }
    }
    
    class PipelineStream extends TransformStream {
      constructor(transformStreams, ...strategies) {
        super({}, ...strategies);
    
        const readable = [super.readable, ...transformStreams]
          .reduce((readable, transform) => readable.pipeThrough(transform));
    
        Object.defineProperty(this, "readable", {
          get() {
            return readable;
          }
        });
      }
    }
    
    const TEXT_CHUNK =
     "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy " +
     "nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.";
    const TEXT_CONTENT = new Array(1024 * 1024).fill(TEXT_CHUNK).join("");
    const readableInput = new Blob([TEXT_CONTENT]).stream();
    const readableOuput = readableInput.pipeThrough(new PipelineStream([
      new TextDecoderStream(),
      new UpperCaseTextStream(),
      new TextEncoderStream()]));
    (async () => {
      const text = await new Response(readableOuput).text();
      console.log("Test OK?", text == TEXT_CONTENT.toUpperCase());
    })().catch(console.error);

    【讨论】:

    • 你知道这个 PipelineStream 是否能够在里面的最后一个 TransformStream 入队当前块之前接受下一个块吗?我正在尝试一种稍微不同的方法,发现 PipelineStream 等待每个内部 TransformStream 完成,然后再接受下一个块并再次调用 transform(),这使得它的优化程度低于显式链接多个 .pipeThroughs。
    • 我认为这是不可能的。您仍然可以尝试 ping 规范作者,这就是我在实施解决方案之前所做的。这帮助我验证了它(我在答案之前找到了解决方案),请参阅twitter.com/check_ca/status/1544817673560784896twitter.com/jaffathecake/status/1545387422414180353
    • 顺便说一句,您可以将此问题标记为已回答;)
    • 感谢您的澄清,是的,这是最好的答案。欣赏它!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-26
    • 2021-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多