Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Streams APIをちゃんと理解する

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for kato takeshi kato takeshi
November 30, 2019

Streams APIをちゃんと理解する

JSConf JP 2019のセッションでお話しした内容です。

https://jsconf.jp/2019/talk/takeshi-kato

Avatar for kato takeshi

kato takeshi

November 30, 2019
Tweet

Other Decks in Programming

Transcript

  1. const stream = new ReadableStream( { // ͜ͷΦϒδΣΫτ͕Underlying Source start(controller)

    { // ReadableStreamDefaultController controller.enqueue(chunk); // QueʹChunkΛొ࿥͢Δ }, pull(controller) {}, cancel(reason) {} } ); const reader = stream.getReader(); // ReadableStreamDefaultReader reader.read().then(function readProcess({done, value}) { if (done) { return; } result += value; reader.read().then(readProcess); });
  2. const stream = new ReadableStream( { // ͜ͷΦϒδΣΫτ͕Underlying Source start(controller)

    { // ReadableStreamDefaultController controller.enqueue(chunk); // QueʹChunkΛొ࿥͢Δ }, pull(controller) {}, cancel(reason) {} } ); const reader = stream.getReader(); // ReadableStreamDefaultReader reader.read().then(function readProcess({done, value}) { if (done) { return; } result += value; reader.read().then(readProcess); });
  3. const stream = new ReadableStream( { // ͜ͷΦϒδΣΫτ͕Underlying Source start(controller)

    { // ReadableStreamDefaultController controller.enqueue(chunk); // QueʹChunkΛొ࿥͢Δ }, pull(controller) {}, cancel(reason) {} } ); const reader = stream.getReader(); // ReadableStreamDefaultReader reader.read().then(function readProcess({done, value}) { if (done) { return; } result += value; reader.read().then(readProcess); });
  4. const stream = new ReadableStream( { // ͜ͷΦϒδΣΫτ͕Underlying Source start(controller)

    { // ReadableStreamDefaultController controller.enqueue(chunk); // QueʹChunkΛొ࿥͢Δ }, pull(controller) {}, cancel(reason) {} } ); const reader = stream.getReader(); // ReadableStreamDefaultReader reader.read().then(function readProcess({done, value}) { if (done) { return; } result += value; reader.read().then(readProcess); });
  5. self.addEventListener('fetch', event => { var stream = new ReadableStream({ async

    start(controller) { let html = await caches.match(‘cache.html').then(res => res.text()); const encoder = new TextEncoder(); let pos = 0, chunkSize = 1; function push() { if (pos >= html.length) { controller.close(); return; } controller.enqueue(encoder.encode(html.slice(pos, pos + chunkSize))); pos += chunkSize; setTimeout(push, 5); } push(); } }); event.respondWith(new Response(stream, { headers: {'Content-Type': ‘text/html'} })); }); ࢀߟ: https://jakearchibald.com/2016/streams-ftw/#creating-one-stream-from-multiple-sources-to-supercharge-page-render-times
  6. self.addEventListener('fetch', event => { var stream = new ReadableStream({ async

    start(controller) { let html = await caches.match(‘cache.html').then(res => res.text()); const encoder = new TextEncoder(); let pos = 0, chunkSize = 1; function push() { if (pos >= html.length) { controller.close(); return; } controller.enqueue(encoder.encode(html.slice(pos, pos + chunkSize))); pos += chunkSize; setTimeout(push, 5); } push(); } }); event.respondWith(new Response(stream, { headers: {'Content-Type': ‘text/html'} })); }); ࢀߟ: https://jakearchibald.com/2016/streams-ftw/#creating-one-stream-from-multiple-sources-to-supercharge-page-render-times
  7. self.addEventListener('fetch', event => { var stream = new ReadableStream({ async

    start(controller) { let html = await caches.match(‘cache.html').then(res => res.text()); const encoder = new TextEncoder(); let pos = 0, chunkSize = 1; function push() { if (pos >= html.length) { controller.close(); return; } controller.enqueue(encoder.encode(html.slice(pos, pos + chunkSize))); pos += chunkSize; setTimeout(push, 5); } push(); } }); event.respondWith(new Response(stream, { headers: {'Content-Type': ‘text/html'} })); }); ࢀߟ: https://jakearchibald.com/2016/streams-ftw/#creating-one-stream-from-multiple-sources-to-supercharge-page-render-times event.respondWith(new Response(stream, { headers: {'Content-Type': ‘text/html’} }));
  8. ``` caches.match(request).then(res => res.body).then(readableStream => { // do something });

    ``` $BDIF"1*࣮ߦޙʹड͚औΕΔ3FTQPOTFͷ#PEZ ΋ʮ3FBEBCMF4USFBNʯ
  9. var stream = new ReadableStream({ start(controller) { const header =

    caches.match('./inc/header.html'); const contents = fetch(event.request.url); const footer = caches.match('./inc/footer.html'); function push(stream) { const reader = stream.getReader(); function read({done, value}) { if (done) return; controller.enqueue(value); return reader.read().then(read); } return reader.read().then(read); } header.then(response => push(response.body)).then(() => contents) .then(response => push(response.body)).then(() => footer) .then(response => push(response.body)).then(() => controller.close()); } }); event.respondWith(new Response(stream, { headers: {'Content-Type': ‘text/html'} })); ࢀߟ: https://gist.github.com/jakearchibald/64e26e7a1d9b06b3fa3ec0383f2b1f91
  10. var stream = new ReadableStream({ start(controller) { const header =

    caches.match('./inc/header.html'); const contents = fetch(event.request.url); const footer = caches.match('./inc/footer.html'); function push(stream) { const reader = stream.getReader(); function read({done, value}) { if (done) return; controller.enqueue(value); return reader.read().then(read); } return reader.read().then(read); } header.then(response => push(response.body)).then(() => contents) .then(response => push(response.body)).then(() => footer) .then(response => push(response.body)).then(() => controller.close()); } }); event.respondWith(new Response(stream, { headers: {'Content-Type': ‘text/html'} })); ࢀߟ: https://gist.github.com/jakearchibald/64e26e7a1d9b06b3fa3ec0383f2b1f91
  11. var stream = new ReadableStream({ start(controller) { const header =

    caches.match('./inc/header.html'); const contents = fetch(event.request.url); const footer = caches.match('./inc/footer.html'); function push(stream) { const reader = stream.getReader(); function read({done, value}) { if (done) return; controller.enqueue(value); return reader.read().then(read); } return reader.read().then(read); } header.then(response => push(response.body)).then(() => contents) .then(response => push(response.body)).then(() => footer) .then(response => push(response.body)).then(() => controller.close()); } }); event.respondWith(new Response(stream, { headers: {'Content-Type': ‘text/html'} })); ࢀߟ: https://gist.github.com/jakearchibald/64e26e7a1d9b06b3fa3ec0383f2b1f91
  12. var stream = new ReadableStream({ start(controller) { const header =

    caches.match('./inc/header.html'); const contents = fetch(event.request.url); const footer = caches.match('./inc/footer.html'); function push(stream) { const reader = stream.getReader(); function read({done, value}) { if (done) return; controller.enqueue(value); return reader.read().then(read); } return reader.read().then(read); } header.then(response => push(response.body)).then(() => contents) .then(response => push(response.body)).then(() => footer) .then(response => push(response.body)).then(() => controller.close()); } }); event.respondWith(new Response(stream, { headers: {'Content-Type': ‘text/html'} })); ࢀߟ: https://gist.github.com/jakearchibald/64e26e7a1d9b06b3fa3ec0383f2b1f91
  13. const stream = new WritableStream( { // ͜ͷΦϒδΣΫτ͕UnderlyingSink start(controller) {},

    write(chunk, controller) { // WritableStreamDefaultController output.value = chunk; // Hello }, close() {}, abort(reason) {} } ); const writer = stream.getWriter(); // WritableStreamDefaultWriter writer.ready.then(() => writer.write('hello')) .then(() => writer.write('world')) .then(() => writer.close()) .then(() => console.log('close writer.'))
  14. const stream = new WritableStream( { // ͜ͷΦϒδΣΫτ͕UnderlyingSink start(controller) {},

    write(chunk, controller) { // WritableStreamDefaultController output.value = chunk; // Hello }, close() {}, abort(reason) {} } ); const writer = stream.getWriter(); // WritableStreamDefaultWriter writer.ready.then(() => writer.write('hello')) .then(() => writer.write('world')) .then(() => writer.close()) .then(() => console.log('close writer.'))
  15. const stream = new WritableStream( { // ͜ͷΦϒδΣΫτ͕UnderlyingSink start(controller) {},

    write(chunk, controller) { // WritableStreamDefaultController output.value = chunk; // Hello }, close() {}, abort(reason) {} } ); const writer = stream.getWriter(); // WritableStreamDefaultWriter writer.ready.then(() => writer.write('hello')) .then(() => writer.write('world')) .then(() => writer.close()) .then(() => console.log('close writer.'))
  16. const stream = new WritableStream( { // ͜ͷΦϒδΣΫτ͕UnderlyingSink start(controller) {},

    write(chunk, controller) { // WritableStreamDefaultController output.value = chunk; // Hello }, close() {}, abort(reason) {} } ); const writer = stream.getWriter(); // WritableStreamDefaultWriter writer.ready.then(() => writer.write('hello')) .then(() => writer.write('world')) .then(() => writer.close()) .then(() => console.log('close writer.'))
  17. fetch('https://streams.spec.whatwg.org/').then(async (res) => { const readableStream = res.body; const fileWriter

    = await handle.createWriter(); // handle͸NativeFileSystemAPI͔ΒಘͨΦϒδΣΫτ let offset = 0; const writableStream = new WritableStream({ start() { return fileWriter.truncate(0); }, write(chunk, controller) { return new Promise(async (resolve, reject) => { try { await fileWriter.write(offset, chunk); offset += chunk.length; resolve(); } catch(err) { controller.error(err); reject(err); } }); }, close() { fileWriter.close(); } }); readableStream.pipeTo(writableStream); })
  18. fetch('https://streams.spec.whatwg.org/').then(async (res) => { const readableStream = res.body; const fileWriter

    = await handle.createWriter(); // handle͸NativeFileSystemAPI͔ΒಘͨΦϒδΣΫτ let offset = 0; const writableStream = new WritableStream({ start() { return fileWriter.truncate(0); }, write(chunk, controller) { return new Promise(async (resolve, reject) => { try { await fileWriter.write(offset, chunk); offset += chunk.length; resolve(); } catch(err) { controller.error(err); reject(err); } }); }, close() { fileWriter.close(); } }); readableStream.pipeTo(writableStream); })
  19. fetch('https://streams.spec.whatwg.org/').then(async (res) => { const readableStream = res.body; const fileWriter

    = await handle.createWriter(); // handle͸NativeFileSystemAPI͔ΒಘͨΦϒδΣΫτ let offset = 0; const writableStream = new WritableStream({ start() { return fileWriter.truncate(0); }, write(chunk, controller) { return new Promise(async (resolve, reject) => { try { await fileWriter.write(offset, chunk); offset += chunk.length; resolve(); } catch(err) { controller.error(err); reject(err); } }); }, close() { fileWriter.close(); } }); readableStream.pipeTo(writableStream); })
  20. fetch('https://streams.spec.whatwg.org/').then(async (res) => { const readableStream = res.body; const fileWriter

    = await handle.createWriter(); // handle͸NativeFileSystemAPI͔ΒಘͨΦϒδΣΫτ let offset = 0; const writableStream = new WritableStream({ start() { return fileWriter.truncate(0); }, write(chunk, controller) { return new Promise(async (resolve, reject) => { try { await fileWriter.write(offset, chunk); offset += chunk.length; resolve(); } catch(err) { controller.error(err); reject(err); } }); }, close() { fileWriter.close(); } }); readableStream.pipeTo(writableStream); })
  21. const transformStream = new TransformStream( { // ͜ͷΦϒδΣΫτ͕Transformer start(controller) {},

    transform(chunk, controller) { // Τϯίʔυ͞ΕͨChunkΛσίʔυͯ͠Ωϡʔʹొ࿥͢Δ controller.enqueue(new TextDecoder().decode(chunk)); }, flush(controller) { // Writable StreamʹΑΔશͯͷॻ͖ࠐΈ͕ऴΘΓɺ // Writable Stream͕Ϋϩʔζ͠Α͏ͱ͍ͯ͠Δ࣌ʹݺͼग़͞ΕΔ } } ); const {readable, writable} = transformStream; writable.getWriter().write( new Uint8Array([104,101,108,108,111,32,119,111,114,108,100]) ); readable.getReader().read().then(({done, value}) => { console.log(value); // Hello World });
  22. const transformStream = new TransformStream( { // ͜ͷΦϒδΣΫτ͕Transformer start(controller) {},

    transform(chunk, controller) { // Τϯίʔυ͞ΕͨChunkΛσίʔυͯ͠Ωϡʔʹొ࿥͢Δ controller.enqueue(new TextDecoder().decode(chunk)); }, flush(controller) { // Writable StreamʹΑΔશͯͷॻ͖ࠐΈ͕ऴΘΓɺ // Writable Stream͕Ϋϩʔζ͠Α͏ͱ͍ͯ͠Δ࣌ʹݺͼग़͞ΕΔ } } ); const {readable, writable} = transformStream; writable.getWriter().write( new Uint8Array([104,101,108,108,111,32,119,111,114,108,100]) ); readable.getReader().read().then(({done, value}) => { console.log(value); // Hello World });
  23. const transformStream = new TransformStream( { // ͜ͷΦϒδΣΫτ͕Transformer start(controller) {},

    transform(chunk, controller) { // Τϯίʔυ͞ΕͨChunkΛσίʔυͯ͠Ωϡʔʹొ࿥͢Δ controller.enqueue(new TextDecoder().decode(chunk)); }, flush(controller) { // Writable StreamʹΑΔશͯͷॻ͖ࠐΈ͕ऴΘΓɺ // Writable Stream͕Ϋϩʔζ͠Α͏ͱ͍ͯ͠Δ࣌ʹݺͼग़͞ΕΔ } } ); const {readable, writable} = transformStream; writable.getWriter().write( new Uint8Array([104,101,108,108,111,32,119,111,114,108,100]) ); readable.getReader().read().then(({done, value}) => { console.log(value); // Hello World });
  24. const transformStream = new TransformStream( { // ͜ͷΦϒδΣΫτ͕Transformer start(controller) {},

    transform(chunk, controller) { // Τϯίʔυ͞ΕͨChunkΛσίʔυͯ͠Ωϡʔʹొ࿥͢Δ controller.enqueue(new TextDecoder().decode(chunk)); }, flush(controller) { // Writable StreamʹΑΔશͯͷॻ͖ࠐΈ͕ऴΘΓɺ // Writable Stream͕Ϋϩʔζ͠Α͏ͱ͍ͯ͠Δ࣌ʹݺͼग़͞ΕΔ } } ); const {readable, writable} = transformStream; writable.getWriter().write( new Uint8Array([104,101,108,108,111,32,119,111,114,108,100]) ); readable.getReader().read().then(({done, value}) => { console.log(value); // Hello World });
  25. const transformStream = new TransformStream( { // ͜ͷΦϒδΣΫτ͕Transformer start(controller) {},

    transform(chunk, controller) { // Τϯίʔυ͞ΕͨChunkΛσίʔυͯ͠Ωϡʔʹొ࿥͢Δ controller.enqueue(new TextDecoder().decode(chunk)); }, flush(controller) { // Writable StreamʹΑΔશͯͷॻ͖ࠐΈ͕ऴΘΓɺ // Writable Stream͕Ϋϩʔζ͠Α͏ͱ͍ͯ͠Δ࣌ʹݺͼग़͞ΕΔ } } ); const {readable, writable} = transformStream; writable.getWriter().write( new Uint8Array([104,101,108,108,111,32,119,111,114,108,100]) ); readable.getReader().read().then(({done, value}) => { console.log(value); // Hello World });
  26. const transformStream = new TransformStream( { // ͜ͷΦϒδΣΫτ͕Transformer start(controller) {},

    transform(chunk, controller) { // Τϯίʔυ͞ΕͨChunkΛσίʔυͯ͠Ωϡʔʹొ࿥͢Δ controller.enqueue(new TextDecoder().decode(chunk)); }, flush(controller) { // Writable StreamʹΑΔશͯͷॻ͖ࠐΈ͕ऴΘΓɺ // Writable Stream͕Ϋϩʔζ͠Α͏ͱ͍ͯ͠Δ࣌ʹݺͼग़͞ΕΔ } } ); const {readable, writable} = transformStream; writable.getWriter().write( new Uint8Array([104,101,108,108,111,32,119,111,114,108,100]) ); readable.getReader().read().then(({done, value}) => { console.log(value); // Hello World });
  27. const textDecoderStream = new TextDecoderStream('utf-8'); const {readable, writable} = textDecoderStream;

    writable.getWriter().write( new Uint8Array([104,101,108,108,111,32,119,111,114,108,100]) ); readable.getReader().read().then(({done, value}) => { console.log(value); // Hello World }); ˞ͨͩ͠ɺ5SBOTGFS4USFBNͷܧঝͰ͸ͳ͍ɻʢಉ͡*'Λඋ͍͑ͯΔ͚ͩʣ ࢀߟIUUQTHJUIVCDPNXIBUXHFODPEJOHJTTVFT
  28. function md2HTMLFactory() { return new TransformStream({ transform(chunk, controller) { controller.enqueue(marked(chunk));

    } }); } const translateStream = new TransformStream({ transform(chunk, controller) { return fetch(`https://translation.googleapis.com/language/translate/v2/?key={API_KEY}`, { method: 'POST', body: JSON.stringify({ q: chunk, target: 'en', format: 'html' }) }).then(res => res.json()).then(res => { controller.enqueue(res.data.translations[0].translatedText); }); } }); input.addEventListener('change', function () { const stream = input.files[0].stream(); const [org, willBeTranslated] = stream.tee(); Promise.all([ org.pipeThrough(new TextDecoderStream('utf-8')).pipeThrough(md2HTMLFactory()) .pipeTo(originalWriter), willBeTranslated.pipeThrough(new TextDecoderStream('utf-8')).pipeThrough(md2HTMLFactory()) .pipeThrough(translateStream) .pipeTo(translatedWriter) ]).then(() => console.log('complete')); });
  29. function md2HTMLFactory() { return new TransformStream({ transform(chunk, controller) { controller.enqueue(marked(chunk));

    } }); } const translateStream = new TransformStream({ transform(chunk, controller) { return fetch(`https://translation.googleapis.com/language/translate/v2/?key={API_KEY}`, { method: 'POST', body: JSON.stringify({ q: chunk, target: 'en', format: 'html' }) }).then(res => res.json()).then(res => { controller.enqueue(res.data.translations[0].translatedText); }); } }); input.addEventListener('change', function () { const stream = input.files[0].stream(); const [org, willBeTranslated] = stream.tee(); Promise.all([ org.pipeThrough(new TextDecoderStream('utf-8')).pipeThrough(md2HTMLFactory()) .pipeTo(originalWriter), willBeTranslated.pipeThrough(new TextDecoderStream('utf-8')).pipeThrough(md2HTMLFactory()) .pipeThrough(translateStream) .pipeTo(translatedWriter) ]).then(() => console.log('complete')); }); function md2HTMLFactory() { return new TransformStream({ transform(chunk, controller) { controller.enqueue(marked(chunk)); } }); }
  30. function md2HTMLFactory() { return new TransformStream({ transform(chunk, controller) { controller.enqueue(marked(chunk));

    } }); } const translateStream = new TransformStream({ transform(chunk, controller) { return fetch(`https://translation.googleapis.com/language/translate/v2/?key={API_KEY}`, { method: 'POST', body: JSON.stringify({ q: chunk, target: 'en', format: 'html' }) }).then(res => res.json()).then(res => { controller.enqueue(res.data.translations[0].translatedText); }); } }); input.addEventListener('change', function () { const stream = input.files[0].stream(); const [org, willBeTranslated] = stream.tee(); Promise.all([ org.pipeThrough(new TextDecoderStream('utf-8')).pipeThrough(md2HTMLFactory()) .pipeTo(originalWriter), willBeTranslated.pipeThrough(new TextDecoderStream('utf-8')).pipeThrough(md2HTMLFactory()) .pipeThrough(translateStream) .pipeTo(translatedWriter) ]).then(() => console.log('complete')); }); const translateStream = new TransformStream({ transform(chunk, controller) { return fetch(`https://translation.googleapis.com/language/translate/v2/?key={API_KEY}`, { method: 'POST', body: JSON.stringify({ q: chunk, target: 'en', format: 'html' }) }).then(res => res.json()).then(res => { controller.enqueue(res.data.translations[0].translatedText); }); } });
  31. function md2HTMLFactory() { return new TransformStream({ transform(chunk, controller) { controller.enqueue(marked(chunk));

    } }); } const translateStream = new TransformStream({ transform(chunk, controller) { return fetch(`https://translation.googleapis.com/language/translate/v2/?key={API_KEY}`, { method: 'POST', body: JSON.stringify({ q: chunk, target: 'en', format: 'html' }) }).then(res => res.json()).then(res => { controller.enqueue(res.data.translations[0].translatedText); }); } }); input.addEventListener('change', function () { const stream = input.files[0].stream(); const [org, willBeTranslated] = stream.tee(); Promise.all([ org.pipeThrough(new TextDecoderStream('utf-8')).pipeThrough(md2HTMLFactory()) .pipeTo(originalWriter), willBeTranslated.pipeThrough(new TextDecoderStream('utf-8')).pipeThrough(md2HTMLFactory()) .pipeThrough(translateStream) .pipeTo(translatedWriter) ]).then(() => console.log('complete')); }); input.addEventListener('change', function () { // input͸[type=“file”]ͷinputཁૉ const stream = input.files[0].stream(); // fileΦϒδΣΫτ͸stream()ͰReadableStream͕औಘͰ͖Δ const [org, willBeTranslated] = stream.tee(); Promise.all([ org.pipeThrough(new TextDecoderStream(‘utf-8')) .pipeThrough(md2HTMLFactory()) .pipeTo(originalWriter), willBeTranslated.pipeThrough(new TextDecoderStream(‘utf-8')) .pipeThrough(md2HTMLFactory()) .pipeThrough(translateStream) .pipeTo(translatedWriter) ]).then(() => console.log('complete')); });
  32. function md2HTMLFactory() { return new TransformStream({ transform(chunk, controller) { controller.enqueue(marked(chunk));

    } }); } const translateStream = new TransformStream({ transform(chunk, controller) { return fetch(`https://translation.googleapis.com/language/translate/v2/?key={API_KEY}`, { method: 'POST', body: JSON.stringify({ q: chunk, target: 'en', format: 'html' }) }).then(res => res.json()).then(res => { controller.enqueue(res.data.translations[0].translatedText); }); } }); input.addEventListener('change', function () { const stream = input.files[0].stream(); const [org, willBeTranslated] = stream.tee(); Promise.all([ org.pipeThrough(new TextDecoderStream('utf-8')).pipeThrough(md2HTMLFactory()) .pipeTo(originalWriter), willBeTranslated.pipeThrough(new TextDecoderStream('utf-8')).pipeThrough(md2HTMLFactory()) .pipeThrough(translateStream) .pipeTo(translatedWriter) ]).then(() => console.log('complete')); }); input.addEventListener('change', function () { // input͸[type=“file”]ͷinputཁૉ const stream = input.files[0].stream(); // fileΦϒδΣΫτ͸stream()ͰReadableStream͕औಘͰ͖Δ const [org, willBeTranslated] = stream.tee(); Promise.all([ org.pipeThrough(new TextDecoderStream(‘utf-8')) .pipeThrough(md2HTMLFactory()) .pipeTo(originalWriter), willBeTranslated.pipeThrough(new TextDecoderStream(‘utf-8')) .pipeThrough(md2HTMLFactory()) .pipeThrough(translateStream) .pipeTo(translatedWriter) ]).then(() => console.log('complete')); });
  33. 3FBEBCMF4USFBNɾ8SJUBCMF4USFBNΛར༻͢ΔΞΫςΟϒͳಡΈऔ Γػͱॻ͖ࠐΈػ͕͋Δؒɺ֤ετϦʔϜ͸ʮϩοΫʯ͞Εɺղ์͞ΕΔ ·Ͱଞ͔ΒετϦʔϜར༻Ͱ͖ͳ͘ͳΔɻ const readable = new ReadableStream(); const writable

    = new WritableStream(); const reader = readable.getReader(); const writer = writable.getWriter(); console.log(readable.locked); // true console.log(writable.locked); // true readable.getReader(); // error! MPDLFEͱUFF
  34. 1VTI4PVSDF new ReadableStream({ start(controller) { socket.addEventListener('message', (e) => { controller.enqueue(e.data);

    }); } }); 1VMM4PVSDF new ReadableStream({ pull(controller) { controller.enqueue(Math.random()); } }); ˞TUBSUΛ࢖͍ͬͯΕ͹1VTIɺQVMMΛ࢖͍ͬͯΕ͹1VMMͱ͍͏Θ͚Ͱ͸͋Γ·ͤΜɻ
  35. 1VTI4PVSDF new ReadableStream({ start(controller) { socket.addEventListener('message', (e) => { controller.enqueue(e.data);

    }); } }); 1VMM4PVSDF new ReadableStream({ pull(controller) { controller.enqueue(Math.random()); } }); QVMMϝιου͸ Ωϡʔʹۭ͖͕ͳ͘ͳΔ·Ͱ ࣮ߦ͞ΕΔ
  36. 1VTI4PVSDF new ReadableStream({ start(controller) { socket.addEventListener('message', (e) => { controller.enqueue(e.data);

    }); } }); 1VMM4PVSDF new ReadableStream({ pull(controller) { controller.enqueue(Math.random()); } }); QVMMϝιου͸ Ωϡʔʹۭ͖ʢʁʣ͕ͳ͘ͳΔ·Ͱ ࣮ߦ͞ΕΔ
  37. ɾ4USFBNT-JWJOH4UBOEBSE೔ຊޠ༁  IUUQTUSJQMFVOEFSTDPSFHJUIVCJP4USFBNTKBIUNM  ɾTUSFBNCFUXFFOOPEFKTBOEXIBUXH  IUUQTTQFBLFSEFDLDPNKYDLTUSFBNCFUXFFOOPEFKTBOEXIBUXH  ɾ4USFBN:PVS8BZUP*NNFEJBUF3FTQPOTFT 

    IUUQTEFWFMPQFSTHPPHMFDPNXFCVQEBUFTTX SFBEBCMFTUSFBNT  ɾࠓ೔ͷίʔυαϯϓϧͳͲ  IUUQTHJUIVCDPNULTLUPTUSFBNFYBNQMFT ࢀߟࢿྉ