流实现者的 API


node:stream 模块 API 旨在使使用 JavaScript 的原型继承模型轻松实现流成为可能。

首先,流开发者将声明新的 JavaScript 类,该类扩展四个基本流类(stream.Writablestream.Readablestream.Duplexstream.Transform)之一,确保它们调用适当的父类构造函数:

const { Writable } = require('node:stream');

class MyWritable extends Writable {
  constructor({ highWaterMark, ...options }) {
    super({ highWaterMark });
    // ...
  }
}

当扩展流时,在将这些选项转发给基本构造函数之前,请记住用户可以和应该提供哪些选项。 例如,如果实现对 autoDestroyemitClose 选项做出假设,则不允许用户覆盖这些。 显式转发哪些选项,而不是隐式转发所有选项。

然后,新的流类必须实现一个或多个特定方法,具体取决于正在创建的流的类型,如下图所示:

Use-caseClassMethod(s) to implement
只读Readable_read()
只写Writable_write(), _writev(), _final()
可读可写Duplex_read(), _write(), _writev(), _final()
对写入的数据进行操作,然后读取结果Transform_transform(), _flush(), _final()

流的实现代码永远不应该调用供消费者使用的流的“公共”方法(如流消费者的 API 章节所述)。 这样做可能会对使用流的应用程序代码产生不利的副作用。

避免覆盖公共方法,例如 write()end()cork()uncork()read()destroy(),或触发内部事件,例如 'error''data''end''finish''close'.emit()。 这样做可能会破坏当前和未来的流的不变量,从而导致行为和/或与其他流、流实用程序和用户期望的兼容性问题。

The node:stream module API has been designed to make it possible to easily implement streams using JavaScript's prototypal inheritance model.

First, a stream developer would declare a new JavaScript class that extends one of the four basic stream classes (stream.Writable, stream.Readable, stream.Duplex, or stream.Transform), making sure they call the appropriate parent class constructor:

const { Writable } = require('node:stream');

class MyWritable extends Writable {
  constructor({ highWaterMark, ...options }) {
    super({ highWaterMark });
    // ...
  }
}

When extending streams, keep in mind what options the user can and should provide before forwarding these to the base constructor. For example, if the implementation makes assumptions in regard to the autoDestroy and emitClose options, do not allow the user to override these. Be explicit about what options are forwarded instead of implicitly forwarding all options.

The new stream class must then implement one or more specific methods, depending on the type of stream being created, as detailed in the chart below:

Use-caseClassMethod(s) to implement
Reading onlyReadable_read()
Writing onlyWritable_write(), _writev(), _final()
Reading and writingDuplex_read(), _write(), _writev(), _final()
Operate on written data, then read the resultTransform_transform(), _flush(), _final()

The implementation code for a stream should never call the "public" methods of a stream that are intended for use by consumers (as described in the API for stream consumers section). Doing so may lead to adverse side effects in application code consuming the stream.

Avoid overriding public methods such as write(), end(), cork(), uncork(), read() and destroy(), or emitting internal events such as 'error', 'data', 'end', 'finish' and 'close' through .emit(). Doing so can break current and future stream invariants leading to behavior and/or compatibility issues with other streams, stream utilities, and user expectations.