Pro Yearly is on sale from $80 to $50! »

Streams APIをちゃんと理解する

D310bc50113a44636c440973ce497c26?s=47 kato takeshi
November 30, 2019

Streams APIをちゃんと理解する

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

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

D310bc50113a44636c440973ce497c26?s=128

kato takeshi

November 30, 2019
Tweet

Transcript

  1. ʮ4USFBNΛͪΌΜͱཧղ͢Δʯ 

  2. 4BVOB4QB)FBMUI"EWJTFS⽂ Takeshi Kato Frontend Developer in

  3. ʮ4USFBNΛ੍͢Δ΋ͷ͸ɺ/PEFKTΛ੍͢ʯ IUUQKYDLIBUFOBCMPHDPNFOUSZ

  4. ʮ4USFBNT"1*ʯ TJODF0DU 4USFBNT˜8)"58( "QQMF (PPHMF .P[JMMB .JDSPTPGU  -JDFOTFEVOEFS$$#:

  5. ʮ4USFBNT"1*ʯ TJODF0DU

  6. 4USFBNʁ ετϦʔϜʢӳTUSFBNʣͱ͸ɺ ࿈ଓͨ͠σʔλΛʮྲྀΕΔ΋ͷʯͱͯ͠ଊ͑ɺ ͦͷσʔλͷೖग़ྗ͋Δ͍͸ૹड৴Λѻ͏͜ͱͰ͋Γɺ ·ͨͦͷૢ࡞ͷͨΊͷந৅σʔλܕΛࢦ͢ɻ ग़యϑϦʔඦՊࣄయʰ΢ΟΩϖσΟΞʢ8JLJQFEJBʣʱ

  7. αʔό ॲཧ ඳը αʔό ॲཧ ඳը XJUI4USFBN XJUIPVU4USFBN

  8. αʔό αʔό XJUI4USFBN XJUIPVU4USFBN ॲཧ ඳը ॲཧ ඳը

  9. 3FBEBCMF 4USFBN 5SBOTGPSN 4USFBN 8SJUBCMF 4USFBN ʮ4USFBNT"1*ʯ

  10. 5SBOTGPSN 4USFBN 8SJUBCMF 4USFBN 3FBEBCMF 4USFBN

  11. 3FBEBCMF4USFBNʢಡΈࠐΈՄೳͳετϦʔϜʣ 2VF $IVOL 6OEFSMZJOH4PVSDF %BUB ɾ6OEFSMZJOH4PVSDF ɹσʔλͷݯͱͳΔΦϒδΣΫτɻը૾΍ಈըͳͲͷϝσΟΞ͚ͩͰͳ͘ ɹςΩετɺ਺஋ͳͲ΋ѻ͑Δɻ ɾ\3FBEBCMF4USFBN%FGBVMU$POUSPMMFS^ ɹετϦʔϜͷঢ়ଶ΍಺෦Ωϡʔͷ੍ޚͳͲΛߦ͏Ϋϥεɻᶃͷૢ࡞Λ୲౰͢Δɻ

    ɾ\3FBEBCMF4USFBN%FGBVMU3FBEFS^ ɹετϦʔϜ͔ΒνϟϯΫΛಡΈऔΔΫϥεɻᶄͷૢ࡞Λ୲౰͢Δɻ ᶃ ᶄ
  12. 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); });
  13. 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); });
  14. 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); });
  15. 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); });
  16. $BTFʮ4USFBNJOH3FTQPOTFʯ

  17. 'FUDI"1*࣮ߦޙʹड͚औΕΔ3FTQPOTFͷ#PEZ ͸ʮ3FBEBCMF4USFBNʯ ``` fetch(url).then(response => response.body).then(readableStream => { // do

    something }); ```
  18. 4FSWJDF8PSLFSͰϦΫΤετΛΠϯλʔηϓτͯ͠ɺ 4USFBNΛ3FTQPOTFͱͯ͠ฦ͢

  19. 4BNQMF

  20. 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
  21. 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
  22. 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’} }));
  23. ``` caches.match(request).then(res => res.body).then(readableStream => { // do something });

    ``` $BDIF"1*࣮ߦޙʹड͚औΕΔ3FTQPOTFͷ#PEZ ΋ʮ3FBEBCMF4USFBNʯ
  24. ϔομʔʢDBDIFʣ  ίϯςϯπʢGFUDIʣ  ϑολʔʢDBDIFʣ ͷΑ͏ͳ͜ͱ΋Ͱ͖Δ

  25. 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
  26. 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
  27. 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
  28. 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
  29. 3FBEBCMF4USFBNΛ࢖͏ͱ σʔλΛ෼ׂͯ͠গͣͭ͠ ಡΈࠐΉ͜ͱ͕Ͱ͖Δ

  30. 5SBOTGPSN 4USFBN 3FBEBCMF 4USFBN 8SJUBCMF 4USFBN

  31. 8SJUBCMF4USFBNʢॻ͖ࠐΈՄೳͳετϦʔϜʣ 2VF 6OEFSMZJOH4JOL /FX%BUB ɾ\6OEFSMZJOH4JOL^ ɹΩϡʔ͔ΒνϟϯΫΛड͚औͬͯॻ͖ࠐΈΛߦ͏ΦϒδΣΫτɻ ɾ\8SJUBCMF4USFBN%FGBVMU8SJUFS^ ɹ8SJUBCMF4USFBNʹ௚઀νϟϯΫΛॻ͖ࠐΉ͜ͱ͕Ͱ͖ΔΫϥεɻ ɹᶃͷૢ࡞Λ୲౰͢Δɻ ɾ\8SJUBCMF4USFBN%FGBVMU$POUSPMMFS^

    ɹετϦʔϜͷঢ়ଶ΍಺෦Ωϡʔͷ੍ޚͳͲΛߦ͏Ϋϥεɻᶄͷૢ࡞Λ୲౰͢Δɻ ᶄ ᶃ
  32. 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.'))
  33. 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.'))
  34. 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.'))
  35. 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.'))
  36. 3FBEBCMF 4USFBN 8SJUBCMF 4USFBN QJQF5P

  37. $BTFʮ/BUJWF'JMF4ZTUFNʯ ʮϑΝΠϧΛ'FUDIͯ͠ɺυϥΠϒʹ௚઀ॻ͖ࠐΈʯ

  38. 4BNQMF

  39. 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); })
  40. 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); })
  41. 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); })
  42. 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); })
  43. /BUJWF'JMF4ZTUFN Y 4USFBNT"1* IUUQTHJUIVCDPNXJDHOBUJWFpMFTZTUFNJTTVFT 4USFBNΛ࢖༻ͨ͠ॻ͖ࠐΈͷ࣮૷͕ʢଟ෼ʣਐΜͰ͍Δ

  44. 8SJUBCMF4USFBNΛ࢖͏ͱ ෼ׂ͞ΕͨσʔλΛ Ұͭͣͭɺॱ൪ʹ ॻ͖ࠐΉ͜ͱ͕Ͱ͖Δ

  45. 3FBEBCMF 4USFBN 8SJUBCMF 4USFBN 5SBOTGPSN 4USFBN

  46. 5SBOTGPSN4USFBNɹʢม׵ετϦʔϜʣ 8SJUBCMF4USFBN 5SBOTGPSNFS 3FBEBCMF4USFBN ɾ\XSJUBCMF SFBEBCMF^ ɹ8SJUBCMF4USFBNͱ3FBEBCMF4USFBNΛ಺แ͍ͯ͠Δɻ ɾ\5SBOTGPSNFS^ ɹ8SJUBCMF4USFBN͔Β$IVOLΛड͚औͬͯɺม׵ॲཧΛߦ͏ΦϒδΣΫτɻ ɾ\5SBOTGPSN4USFBN%FGBVMU$POUSPMMFS^

    ɹ8SJUBCMFɺ3FBEBCMF4USFBNͷঢ়ଶ΍Ωϡʔͷ੍ޚͳͲΛߦ͏Ϋϥεɻ
  47. 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 });
  48. 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 });
  49. 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 });
  50. 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 });
  51. 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 });
  52. 3FBEBCMF 4USFBN 5SBOTGPSN 4USFBN 8SJUBCMF 4USFBN QJQF5ISPVHI  3&563/3FBEBCMF4USFBN QJQF5P

     3&563/1SPNJTF
  53. 5FYU&ODPEFS4USFBNɾ5FYU%FDPEFS4USFBN GSPN&ODPEJOH"1*

  54. 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 });
  55. 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
  56. $BTFʮ຋༁ʯ

  57. 4BNQMF

  58. 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')); });
  59. 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)); } }); }
  60. 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); }); } });
  61. 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')); });
  62. 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')); });
  63. 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
  64. ಉ͡3FBEBCMFετϦʔϜΛ࢖͍͍ͨ৔߹͸ೋຢԽʢUFFʣ͢Δඞཁ͕͋ Γ·͢ɻ ྫʣJOQVU<UZQFlpMFz>ͰΞοϓϩʔυͨ͠ϑΝΠϧΛTUSFBNͰѻ͏ྫ MPDLFEͱUFF input.addEventListener('change', function () { const stream

    = input.files[0].stream(); // ͓͓΋ͱΛڞ༗ͨ͠2ͭͷStreamͷ഑ྻ͕ಘΒΕΔ const [org, translated] = stream.tee(); … });
  65. 5SBOTGPSN4USFBNΛ࢖͏ͱ νϟϯΫΛྲྀΕΔΑ͏ʹม׵͠ɺ ͞ΒʹಡΈࠐΈ௚͢͜ͱ͕Ͱ͖Δ

  66. 3FBEBCMF 4USFBN 8SJUBCMF 4USFBN 5SBOTGPSN 4USFBN

  67. ͦͷଞͷ֓೦ͱ"1* ɾഎѹʢ#BDL1SFTTVSFʣ ɾ1VTI4PVSDFͱ1VMM4PVSDF ɾ2VFVJOH4USBUFHZ

  68. 3FBEBCMF 4USFBN 8SJUBCMF 4USFBN #BDL1SFTTVSFͱ͸ ޙΖ͕٧·ͬͨʢΩϡʔʹۭ͖͕ͳ͘ͳͬͨʣ࣌ʹɺ

  69. 8SJUBCMF 4USFBN 3FBEBCMF 4USFBN #BDL1SFTTVSFͱ͸ ޙΖ͕٧·ͬͨʢΩϡʔʹۭ͖͕ͳ͘ͳͬͨʣ࣌ʹɺ ύΠϓΛ௨ͯ͠લͷετϦʔϜʹ௨஌͢Δ

  70. ɾ1VTI4PVSDF ɹ4PVSDFଆ͔ΒࣗൃతʹFORVFVF͢ΔλΠϓ FH4PDLFU ɾ1VMM4PVSDF ɹϦΫΤετʹԠͯ͡FORVFVF͢ΔλΠϓ FHϑΝΠϧϋϯυϧ ɹ 6OEFSMZJOH4PVSDFͷͭͷλΠϓ

  71. 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ͱ͍͏Θ͚Ͱ͸͋Γ·ͤΜɻ
  72. 1VTI4PVSDF new ReadableStream({ start(controller) { socket.addEventListener('message', (e) => { controller.enqueue(e.data);

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

    }); } }); 1VMM4PVSDF new ReadableStream({ pull(controller) { controller.enqueue(Math.random()); } }); QVMMϝιου͸ Ωϡʔʹۭ͖ʢʁʣ͕ͳ͘ͳΔ·Ͱ ࣮ߦ͞ΕΔ
  74. ֤ετϦʔϜ͕࣋ͭΩϡʔʹอ࣋Ͱ͖ΔαΠζͱ࠷େ਺ʢͭ·Γۭ͖ʣΛ ܾΊΔΦϒδΣΫτɻΦϒδΣΫτ͸ҎԼͷϓϩύςΟΛ࣋ͭɻ ɾIJHI8BUFS.BSLOVNCFS ɹετϦʔϜͷݶքਫҐʢΩϟύγςΟʣΛࢦఆ͢Δɻɹ )8.ʢΩϡʔʹొ࿥͞Ε͍ͯΔνϟϯΫͷ߹ܭαΠζʣ ɾTJ[F DIVOL OVNCFS ɹԿΛ΋ͬͯΩϡʔͷαΠζΛදݱ͢Δ͔Λܾఆ͢Δؔ਺ɻ 2VFVJOH4USBUFHZ

  75. ྫʣDIVOLΛจࣈྻͱͯ͠ड͚औΔ͜ͱΛ૝ఆͨ͠৔߹ɺ ɹ͜ͷ4USFBN͸߹ܭจࣈ·Ͱ͔͠Ωϡʔʹొ࿥͢Δ͜ͱ͕Ͱ͖ͳ͍ɻ new ReadableStream({ pull(controller) { controller.enqueue('pull'); } },{ size(chunk)

    { return chunk.length; }, highWaterMark: 100 }); 2VFVJOH4USBUFHZ
  76. ʮ4USFBNT"1*ʯ TJODF0DU 4USFBNT˜8)"58( "QQMF (PPHMF .P[JMMB .JDSPTPGU  -JDFOTFEVOEFS$$#:

  77. 4USFBNTʢͱͦͷपลʣͷࠓޙ

  78. (JTDPNJOHUP+BQBOJO ɾߴ଎ɺେ༰ྔ ɾ௿஗Ԇ ɾଟ઀ଓ

  79. ωοτϫʔΫʹؔ͢ΔύϑΥʔϚϯεվળࡦ ɾͰ͖Δ͚ͩϦΫΤετ਺΍ϑΝΠϧαΠζΛݮΒ͢ ɹɾεϓϥΠτը૾Λ࢖͏ ɹɾςΩετϑΝΠϧΛѹॖ͢Δ ɾQSFMPBE QSFGFUDI ɾΩϟογϡɺ$%/ͷར༻ ͜Ε·ͰϘτϧωοΫͱ͞Ε͖ͯͨ΋ͷ͕(ʹΑͬͯ վળ͞ΕΔɻ

  80. ʮ(ͩ͠ɺϑΝΠϧαΠζͳΜͯؔ܎ͳ͍ΑͶ ʯ େ༰ྔͷϑΝΠϧ͕഑৴͞Εɺ ΫϥΠΞϯτͷॲཧ͕௥͍͔ͭͳ͘ͳΔ͔΋ʜʁ ʮΫϥ΢υήʔϛϯάαʔϏε࢝Ί͍ͨʂʯ ʮϥΠϒετϦʔϛϯάͰ഑৴͍ͨ͠ʂʯ

  81. ʮ8FC$PEFDTʯ ϒϥ΢β͕උ͑ΔίʔσοΫ΍ίϯςφΛར༻͢ΔͨΊͷ"1*

  82. ɾ4USFBNT-JWJOH4UBOEBSE೔ຊޠ༁  IUUQTUSJQMFVOEFSTDPSFHJUIVCJP4USFBNTKBIUNM  ɾTUSFBNCFUXFFOOPEFKTBOEXIBUXH  IUUQTTQFBLFSEFDLDPNKYDLTUSFBNCFUXFFOOPEFKTBOEXIBUXH  ɾ4USFBN:PVS8BZUP*NNFEJBUF3FTQPOTFT 

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