WritableStream()

Baseline 2022

Newly available

Since June 2022, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

WritableStream() 构造函数创建一个新的 WritableStream 对象实例。

语法

js
new WritableStream(underlyingSink)
new WritableStream(underlyingSink, queuingStrategy)

参数

underlyingSink 可选

一个包含方法和属性的对象,这些方法和属性定义了构造的流的实例的具体行为。underlyingSource 可以包括:

start(controller) 可选

这是一个当对象被构造时立刻调用的方法。此方法的内容由开发人员定义,并应着眼于访问流,并执行其他任何必需的设置流功能。如果这个过程是异步完成的,它可以返回一个 promise,以表明异步操作成功或失败。传递给这个方法的 controller 参数是一个 WritableStreamDefaultController。开发人员可以在设置时使用它来控制流。

write(chunk, controller) 可选

当一个新的数据块(指定为 chunk 参数传入)准备好写入底层接收器时,将调用此方法,该方法也由开发人员定义。它可以返回一个 promise 来表示写入操作的成功或者失败。传递给这个方法的 controller 参数是一个 WritableStreamDefaultController,当提交了更多的块进行写入时,开发人员可以使用它来控制流。这个方法仅在上一次写入成功后才会被再次调用,并且永远不会在流关闭或者中止后被调用(见下文)。

close(controller) 可选

如果应用程序发出已经完成了所有分块的写入的信号,将调用此方法,该方法也是由开发人员定义。其应该完成向底层接收器的数据写入,然后释放对它的访问。如果这个过程是异步完成的,它可以返回一个 promise,以表明操作成功或失败。这个方法只有在所有等待的写入操作都成功后才会被调用。传递给这个方法的 controller 参数是一个 WritableStreamDefaultController,可用于写入结束时控制流。

abort(reason) 可选

如果应用程序发出希望立即关闭流并且将其移至错误状态的信号,将调用此方法,该方法也是由开发人员定义。它可以清理任何被占用的资源,就像 close() 一样,但是即使存在等待的写入操作,abort() 也将被调用——那些分块将被丢弃。如果这个过程是异步完成的,它可以返回一个 promise,以表明操作成功或失败。reason 参数包含一个字符串,用于指定流被中止的原因。

queuingStrategy 可选

一个可选的定义流的队列策略的对象。这需要两个参数:

highWaterMark

非负整数——这定义了在应用背压之前可以包含在内部队列中的分块的最大数量。

size(chunk)

包含参数 chunk 的方法——这表示每个分块所需要使用的字节数。

备注: 你可以定义一个自己的 queuingStrategy,或者为这个对象值使用 ByteLengthQueuingStrategyCountQueuingStrategy 的实例。如果没有提供 queuingStrategy,则使用的默认值与 CountQueuingStrategy 相同,其 highWaterMark 为 1。

返回值

WritableStream 对象的一个实例。

示例

下面的例子说明了这个接口的几个功能。它展示了使用自定义接收器和由 API 提供的队列策略创建的 WritableStream。然后它调用一个 sendMessage() 的函数,传递新创建的流和一个字符串。在这个函数内部,它调用流的 getWriter() 方法,该方法返回一个 WritableStreamDefaultWriter 实例。forEach() 用于将字符串的每个分块写入流。最后,write()close() 方法都会返回 promise,promise 的状态由对应的操作是否成功来决定。

js
const list = document.querySelector("ul");

function sendMessage(message, writableStream) {
  // defaultWriter is of type WritableStreamDefaultWriter
  const defaultWriter = writableStream.getWriter();
  const encoder = new TextEncoder();
  const encoded = encoder.encode(message, { stream: true });
  encoded.forEach((chunk) => {
    defaultWriter.ready
      .then(() => {
        return defaultWriter.write(chunk);
      })
      .then(() => {
        console.log("Chunk written to sink.");
      })
      .catch((err) => {
        console.log("Chunk error:", err);
      });
  });
  // Call ready again to ensure that all chunks are written
  //   before closing the writer.
  defaultWriter.ready
    .then(() => {
      defaultWriter.close();
    })
    .then(() => {
      console.log("All chunks written");
    })
    .catch((err) => {
      console.log("Stream error:", err);
    });
}

const decoder = new TextDecoder("utf-8");
const queuingStrategy = new CountQueuingStrategy({ highWaterMark: 1 });
let result = "";
const writableStream = new WritableStream(
  {
    // Implement the sink
    write(chunk) {
      return new Promise((resolve, reject) => {
        var buffer = new ArrayBuffer(1);
        var view = new Uint8Array(buffer);
        view[0] = chunk;
        var decoded = decoder.decode(view, { stream: true });
        var listItem = document.createElement("li");
        listItem.textContent = "Chunk decoded: " + decoded;
        list.appendChild(listItem);
        result += decoded;
        resolve();
      });
    },
    close() {
      var listItem = document.createElement("li");
      listItem.textContent = "[MESSAGE RECEIVED] " + result;
      list.appendChild(listItem);
    },
    abort(err) {
      console.log("Sink error:", err);
    },
  },
  queuingStrategy,
);
sendMessage("Hello, world.", writableStream);

你可以在我们的简单的 writer 示例找到完整代码。

背压

由于 API 支持背压的方式(其在代码中的实现)可能不太明显。要了解背压是如何实现的,请注意以下三点:

  • 为创建计数策略(第 35 行)而设置的 highWaterMark 属性,其用于设置 WritableStream 实例处理单个 write() 操作时可接受的最大数据量。在该示例中,它是可以传递给 defaultWriter.write() 的最大数据量(第 11 行)。
  • defaultWriter.ready 属性返回一个当 sink(WritableStream 构造函数的第一个属性)完成写入数据时兑现的 promise。数据源可以写入更多的数据(第 9 行)或者调用 close()(第 24 行)。过早调用 close() 会阻止数据写入。这就是示例调用 defaultWriter.ready 两次的原因(第 9 行和第 22 行)。
  • 接收器的 write() 方法(第 40 行)返回的 Promise 告诉 WritableStream 和它的 writer 何时去兑现 defaultWriter.ready

规范

Specification
Streams Standard
# ref-for-ws-constructor④

浏览器兼容性

BCD tables only load in the browser