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.

Stream APIWritableStream 接口为将流数据写入目的地(称为接收器)提供了一个标准的抽象。该对象带有内置的背压和队列。

WritableStream 是一个可转移对象

构造函数

WritableStream()

创造一个新的 WritableStream 对象。

实例属性

WritableStream.locked 只读

一个布尔值,表示 WritableStream 是否锁定到一个 writer。

实例方法

WritableStream.abort()

中止流,使得生产者不能再成功向流写入数据(会立刻返回一个错误状态),并丢弃所有已入队的数据。

WritableStream.close()

关闭流。

WritableStream.getWriter()

返回一个新的 WritableStreamDefaultWriter 实例并且将流锁定到该实例。当流被锁定时,直到这个流被释放之前,不能获取其他 writer。

示例

下面的例子说明了这个接口的几个功能。它展示了使用自定义 sink 和由 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
# ws-class

浏览器兼容性

BCD tables only load in the browser

参见