ReadableStream

Baseline 广泛可用 *

此功能已成熟,并可在多种设备和浏览器版本上使用。自 ⁨2019 年 1 月⁩起,它已在所有浏览器中可用。

* 此特性的某些部分可能存在不同级别的支持。

注意:此功能在 Web Workers 中可用。

ReadableStream 接口是 Streams API 的一部分,代表一个可读的字节数据流。 Fetch API 通过 body 属性提供了一个 ReadableStream 的具体实例,该属性是 Response 对象的一部分。

ReadableStream 是一个 可转移对象

构造函数

ReadableStream()

从给定的处理程序创建并返回一个可读流对象。

实例属性

ReadableStream.locked 只读

返回一个布尔值,指示可读流是否已被读取器锁定。

静态方法

ReadableStream.from() 实验性

从提供的可迭代对象或异步可迭代对象(如数组、集合、异步生成器等)返回 ReadableStream

实例方法

ReadableStream.cancel()

返回一个 Promise,当流被取消时解析。调用此方法表示消费者对该流不再感兴趣。提供的 reason 参数将传递给底层源,源可能会也可能不会使用它。

ReadableStream.getReader()

创建一个读取器并将流锁定到该读取器。当流被锁定后,在释放此读取器之前,不能获取其他读取器。

ReadableStream.pipeThrough()

提供了一种链式方式,将当前流通过转换流或任何其他可写/可读对进行管道传输。

ReadableStream.pipeTo()

将当前 ReadableStream 管道传输到指定的 WritableStream,并返回一个 Promise。当管道传输成功完成时,该 Promise 会 fulfilled;如果遇到任何错误,则会 rejected。

ReadableStream.tee()

tee 方法 此可读流进行分叉,返回一个包含两个新 ReadableStream 实例的两个元素的数组。这两个流接收相同的数据。

异步迭代

ReadableStream 实现 异步迭代协议。这使得可以使用 for await...of 语法异步迭代流中的块。

js
const stream = new ReadableStream(getSomeSource());

for await (const chunk of stream) {
  // Do something with each 'chunk'
}

异步迭代器会消耗流,直到它没有更多数据或终止。循环也可以由于 breakthrowreturn 语句而提前退出。

在迭代期间,流会被锁定,以防止其他消费者获取读取器(尝试迭代一个已被锁定的流将抛出 TypeError)。此锁在循环退出时会被释放。

默认情况下,退出循环也会取消流,使其无法再被使用。要在退出循环后继续使用流,请将 { preventCancel: true } 传递给流的 values() 方法。

js
for await (const chunk of stream.values({ preventCancel: true })) {
  // Do something with 'chunk'
  break;
}
// Acquire a reader for the stream and continue reading ...

示例

Fetch 流

在以下示例中,创建了一个人工 Response 对象,以将从其他资源获取的 HTML 片段流式传输到浏览器。

它演示了 ReadableStreamUint8Array 结合使用的用法。

js
fetch("https://www.example.org")
  .then((response) => response.body)
  .then((rb) => {
    const reader = rb.getReader();

    return new ReadableStream({
      start(controller) {
        // The following function handles each data chunk
        function push() {
          // "done" is a Boolean and value a "Uint8Array"
          reader.read().then(({ done, value }) => {
            // If there is no more data to read
            if (done) {
              console.log("done", done);
              controller.close();
              return;
            }
            // Get the data and send it to the browser via the controller
            controller.enqueue(value);
            // Check chunks by logging to the console
            console.log(done, value);
            push();
          });
        }

        push();
      },
    });
  })
  .then((stream) =>
    // Respond with our stream
    new Response(stream, { headers: { "Content-Type": "text/html" } }).text(),
  )
  .then((result) => {
    // Do things with result
    console.log(result);
  });

将迭代器或异步迭代器转换为流

from() 静态方法可以将迭代器(如 ArrayMap)或 (异步)迭代器 转换为可读流。

js
const myReadableStream = ReadableStream.from(iteratorOrAsyncIterator);

在不支持 from() 方法的浏览器中,您可以改为创建自己的 自定义可读流 来达到相同的效果。

js
function iteratorToStream(iterator) {
  return new ReadableStream({
    async pull(controller) {
      const { value, done } = await iterator.next();

      if (value) {
        controller.enqueue(value);
      }
      if (done) {
        controller.close();
      }
    },
  });
}

警告:此示例假定返回值(在 donetrue 时为 value)如果存在,也应视为要入队的块。某些迭代器 API 可能将返回值用于不同目的。您可能需要根据您交互的 API 来调整代码。

使用 for await...of 对流进行异步迭代

此示例演示了如何使用 for await...of 循环处理 fetch() 响应,以迭代到达的块。

js
const response = await fetch("https://www.example.org");
let total = 0;

// Iterate response.body (a ReadableStream) asynchronously
for await (const chunk of response.body) {
  // Do something with each chunk
  // Here we just accumulate the size of the response.
  total += chunk.length;
}

// Do something with the total
console.log(total);

规范

规范
Streams
# rs-class

浏览器兼容性

另见