0%

学习ReadableStream

前端都是事件响应机制。发送个请求,返回结果后通过某个事件的回调函数处理。如果需要返回的数据太大,则需要一直等待。通过Stream,就能返回一部分,就解析一部分,显示一部分。可以优化体验。

fetch API返回的response.body是一个 ReadableStream对象。ReadableStream对请求大体积文件时非常有用,可以让应答数据,一小段一小段的解析。Web Stream应该是为网络视频,网络直播专门开放的API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// fetch函数是立刻返回一个promise,当HTTP Header的应答到来时,就进入then函数 
fetch(url).then((response) => {
// response.body是ReadableStream.
var reader = response.body.getReader();
var bytesReceived = 0;
return reader.read().then(function processResult(result) {
if (result.done) {
console.log("Fetch complete")
return
}
// result.value for fetch streams is a Uint8Array
bytesReceived += result.value.length
console.log("Received", bytesReceived, "bytes of data so far")
// Read some more, and call this function again
return reader.read().then(processResult)
})
})

这样就是通过对fetch取回的response.body,可以做流式处理,对视频,图片,大文件都有好处,至少能显示进度了。

也可以创建自定义的ReadableStream。ReadableStream在创建时,需要提供underlying source,就是数据源对象。在规范文档中,对source分为push source,和pull source。比如websocket属于push source,而读取文件算是pull source。在规范文档中都有例子。

1
2
3
4
5
6
7
8
var stream = new ReadableStream(
{
start(controller) {},
pull(controller) {},
cancel(reason) {},
},
queuingStrategy,
);

但是这里有几个地方值得注意,规范中貌似没有写清楚,这些内部机制不理解导致很多代码看不懂:

  1. start函数会立即执行
  2. pull函数会在start函数立即执行
  3. 如果start函数返回一个promise对象,则pull函数不再执行,而是等promise对象resolve以后才开始执行pull函数
  4. chunk data可以通过controller.enqueue放入ReadableStream的内部队列中,每次enqueue,都会触发pull函数
  5. 如果pull函数,返回一个promise,则不会每次enqueue都触发pull函数

在pdf.js的代码中,main线程和worker线程之间通过stream,传递pdf数据,很多利用了ReadableStream的特性,这些特性的教程很少。理解起来挺费劲。

优秀参考:

2016 - the year of web streams

Streams Living Standard