Slide 1

Slide 1 text

Stream b/w node.js & whatwg Tokyo Node Gakuen #tng22 2016/8/8 Jxck

Slide 2

Slide 2 text

● id: Jxck ● github: Jxck ● twitter: @jxck_ ● blog: https://blog.jxck.io ● podcast: http://mozaic.fm ● Love: music Jack

Slide 3

Slide 3 text

mozaic.fm ep10 Node.js 3 https://mozaic.fm/episodes/10/nodejs.html

Slide 4

Slide 4 text

4 Node.js の Stream API で「データの流れ」を扱う方法 - Block Rockin’ Codes

Slide 5

Slide 5 text

“Stream を制す者は Node を制す” 5

Slide 6

Slide 6 text

“Stream を制す者は Node を制す” 6 から 5 年...

Slide 7

Slide 7 text

7 WHATWG Streams Stream API がブラウザにやってくる - Block Rockin’ Codes

Slide 8

Slide 8 text

先に結論 8 互換性など無い

Slide 9

Slide 9 text

Stream とは?(哲学) 9

Slide 10

Slide 10 text

Common Terminology 10

Slide 11

Slide 11 text

11 概念としての Stream File Process Network File Process Network Without Stream(データをまとめて次に渡す) With Stream(データを得られた順に次に渡す) then(buffer) pipe(chunk) then(buffer) pipe(chunk)

Slide 12

Slide 12 text

12 両者の供述を聞く

Slide 13

Slide 13 text

● 非同期は Callback が基本 ● 引数にルールを決めて扱いやすくしよう ● 連続するイベントは EventEmitter ● EventEmitte の上位 API が Stream ● Multi-Consumer ● カスタマイズは継承 13 In Node.js Stream data data data data data data data data data data data data

Slide 14

Slide 14 text

14 In WHATWG ● 非同期は Promise を返してこう ● Async/Await でそれを便利にしよう ● もちろん Stream は Promise のジェネレータ ● (≠ ES6 Generator) ● Single-Consumer ● カスタマイズはコンストラクタ Stream Promise

Slide 15

Slide 15 text

diff of Basic Async Handling 15 // Node.js - Callback file.readFile(path, (error, data) => { console.log(error, data); }); // WHATWG(@2016) - Promise cache.match(req).then((res) => { console.log(res); }).catch((error) => { console.error(errror); });

Slide 16

Slide 16 text

node.js stream // ReadableStream extends Stream (extends EventEmitter) readableStream.on(‘data’, (value) => { console.log(value); }); readableStream.on(‘end’, () => { console.log(‘done’); }); readableStream.on(‘error’, (error) => { console.error(error); }); 16

Slide 17

Slide 17 text

whatwg stream let reader = readableStream.getReader(); // read() を呼ぶと chunk を resolve する promise が返る reader.read().then(function processResult({done, value}) { if (done) return; console.log(value); // read() の呼び出しを再帰する return reader.read().then(processResult); }).catch((error) => { console.error(error); }); 17

Slide 18

Slide 18 text

node.js pipe readable .pipe(transform) // return readable .pipe(transform) // return readable .pipe(writable) // return readable .on('end') .on('error'); 18

Slide 19

Slide 19 text

whatwg pipe readable .pipeThrough(transform1) // return readable .pipeThrough(transform2) // return readable .pipeTo(writable) // return promise .then(done) .catch(fail); 19

Slide 20

Slide 20 text

whatwg tee() const [r1, r2] = readableStream.tee(); Promise.all([ r1.pipeTo(cache), // データは同一参照 r2.pipeTo(upload) ]) .then(() => console.log('done')) .catch((err) => console.error(err)); 20

Slide 21

Slide 21 text

Push/Pull Source/Sync ● Source ○ underlying source ○ データの生成元 ● Sink ○ underlying sink ○ データの渡す先 ● Push Underlying Source ○ 勝手に湧き出てくる ○ TCP, Click, Timer etc ● Pull Underlying Source ○ 欲しい時に取り出す ○ File, Random etc 21

Slide 22

Slide 22 text

backpressure ● read の生成が write の消費より早い場合 ○ そのままだと write が間に合わず溢れる ○ write の buffer を見て read を止める方法 ● いつ backpressure をかけるか ○ queuing strategy で決める 22 File Process Network 速 遅 ちょっと待って!

Slide 23

Slide 23 text

queuing stragtegy ● backpressure の戦略 ○ 内部の queue の管理戦略 ○ high-water mark を決める ○ queue の空き < hwm だったら backpressure ● 組み込み queuing strategy ○ count queuing strategy (HWM == count) ○ bytelength queuing strategy (HWM == size) 23 速 遅 二つ空くまでは ストップで HWM

Slide 24

Slide 24 text

独自 Stream socketStream = new ReadableStream({ start(controller) { socket.on('data', (e) => controller.enqueue(e.data)); socket.on('end', () => controller.close()); socket.on('error', (err) => controller.error(err)); }, cancel() { socket.close(); } }); 24

Slide 25

Slide 25 text

backpressure 付き readable socketStream = new ReadableStream({ start(controller) { socket.on('data', (e) => { if (controller.desiredSize <= 0) { socket.readStop(); // backpressure } controller.enqueue(e.data); }); }, pull() { // resume socket.readStart(); }); 25

Slide 26

Slide 26 text

backpressure 付き writable fileStream = new WritableStream({ start() { return fs.open(filename, "w").then(result => { this.fd = result; }); }, write(chunk) { // promise を返す return fs.write(this.fd, chunk, 0, chunk.length); } close() { return fs.close(this.fd); } }); 26

Slide 27

Slide 27 text

Observable ? 27 ● Stream ○ WHATWG Spec ○ あくまで I/O の抽象化 ○ 伝達対象はデータの Chunk ○ backpressure のサポート ○ tee() を覗き、基本 single-comsumer ● Observable ○ ECMAScript Spec ○ 連続イベントの抽象化 ○ イベントそのものだから間引くなどの発想がある ○ backpressure 無し ○ multi-consumer 前提 How do readable streams relate to observables or EventTarget?

Slide 28

Slide 28 text

まとめ 28 ● node - whatwg 間で Stream に互換はない ○ 非同期の表現(promise) から来る差 ○ stream は promise のジェネレータ ○ 変換するなら node2whawg の方が楽そう ● 似てるけど違う用途 ○ whatwg stream は I/O のため ○ observable は event のため ● お気持ち ○ i8c なんてなかった

Slide 29

Slide 29 text

Fin 29

Slide 30

Slide 30 text

Jack