読者です 読者をやめる 読者になる 読者になる

15 min/d

ぼうずやのにっき

WHATWG Streams の覚書 (2)

2016-10-03 も書いたが bouzuya/whatwg-streams-examplewhatwg/streams の参考実装を試している。進捗はほとんどない。src/ から test/ に code を移しているくらいだ。

WHATWG Streams のことを分かる範囲ですこしだけ書いておく。前提として昨日 (2016-10-03) 書いたとおり Model を読んでいるものとする。

WHATWG Streams は 3 つの Stream class が登場する。

  • ReadableStream
  • WritableStream
  • TransformStream

ReadableStream は underlying source の wrapper だ。underlying source から enqueue() された chunk を internal queue に持つ。consumer はそれを読み取る。high-level な操作としては pipeTo()WritableStream に pipe できたり、pipeThrough()TransformStream に pipe できる。ほかに tee() で分岐できる、これはまたあとで触れる。low-level な操作としては getReader()read() できる。high-level な操作も内部では getReader() を使う。

ReadableStream もっとも重要な点は reader をひとつしか持てない (lock) 点だ。ReadableStreamgetReader() を呼び出すと reader が得られる。この reader が lock を開放するまで、ReadableStreamgetReader() を呼び出せない。pipeThrough() / pipeTo() / tee() はすべて getReader() を使用するので同様だ。RxJS Observable や Node.js Stream との一番の違いはここだと思う。

ちなみに tee() の場合は、その ReadableStream を lock する代わりに新たな ReadableStream をふたつ返す。それらを getReader() すれば複数での読み取りは困らないだろう。

WritableStream は underlying sink の wrapper だ。producer から writer で write() された chunk を internal queue に持つ。underlying sink に write() する。ReadableStream の対なので難しい点はない。もちろん getWriter() は low-level な操作だし、 WritableStream を lock する。

TransformStreamReadableStreamWritableStream をまとめたものだ。pipeThrough() に渡す。現状の仕様には何も書かれていない。参考の実装を見た範囲では現状 readablewritable property でそれらを返すようになっているだけだ。pipeThrough() の実装を読むと TransformStream への要求が分かりやすい。

pipeThrough({ writable, readable }, options) {
  this.pipeTo(writable, options);
  return readable;
}

ここまでで分かるとおり ReadableStream / WritableStream / TransformStream で pipe chain をつくる。主な流れは次のとおりだ。 underlying source -> ReadableStream -> TransformStream -> WritableStream -> underlying sink 。

とりあえず今日はここまで。

覚書: underlying source / underlying sink こそ使うことに触れていない。 API について書いてから触れる。back pressure には触れてない。queuing strategy を調べてから触れる。