Share
Embed
Minimum Hands-on Node.js / minimum handson nodejs
Daiki Kuriyama
November 29, 2019
Transcript
53 slides
-
.JOJNVN)BOETPO/PEFKT ձ 3PPN# ࣌ 201912݄01 14:15ʙ16:00 ్த15ͷٳܜΛڬΈ·͢ ϋογϡλά #jsconfjp_b εϐʔΧʔ
ɹɹɹ܀ࢁଠرʢ!"KJEPʣ ɹɹɹϠϑʔגࣜձࣾ›/PEFKTࠇଳ -
.*/*.6.)"/%40//0%&+4 / 53 ௌߨରऀ▶︎+BWB4DSJQUͳΒগ͠Θ͔Δ͚ΕͲ/PEFKT͍·ͻͱͭͱ͍͏ํ ಘΒΕΔ▶︎ࠓ͙͑͢Δ/PEFKTͷجૅࣝ ͜ͷࢿྉͷϦϯΫ https://speakerdeck.com/ajido/minimum-handson-nodejs ԋशϦϯΫ https://chouseisan.com/s?h=4a0d7eb9c38a440e9ad585abfbb16e79 ‣
bit.ly/2XWSQQK εΫϦϓτϦϯΫ https://github.com/ajido/nodejs-handson ϋϯζΦϯதʹ্هϦϯΫϖʔδΛར༻͠·͢ɻࣄલʹ֬͝ೝ͍ͩ͘͞ ! 2 -
+BWB4DSJQU &4 &4ʙ 5ZQF4DSJQU ֶशϚοϓ ηΩϡΞϓϩάϥϛϯά / 53 04 104*9
8)"58("1* %0. FUD 'SPOUFOE 'SBNFXPSLT 8FC 'SBNFXPSLT 1BB4 $BB4 'BB4 /PEFKT$PSF"1* )551)5514 5$1*1 ˔ࠓճͷϋϯζΦϯ༰ 5PPMT %# 5FTU -PHFUD ! 3 -
جૅ Ԡ༻ ‣ Πϯετʔϧ 5 ‣ ϑϩʔ੍ޚ 38 ‣ Ϟδϡʔϧཧ
11 ‣ ඇಉظؔ 42 ‣ ಛ 16 ‣ σόοά ‣ σβΠϯύλʔϯ 21 ‣ Ϟδϡʔϧͷ࡞ ‣ ίʔϧόοΫ 23 ‣ ϓϩϑΝΠϦϯά ‣ Πϕϯτۦಈ 31 ‣ ϑϨʔϜϫʔΫͱπʔϧ ‣ ಉظॲཧ 35 -
දࣔͰ͖ͨΒɺ ԋशϦϯΫͷϖʔδΛ։͘ ʮग़ܽΛೖྗ͢ΔʯΛԡ͢ ࠓճԋशςετͳͷͰʮԋशςετʯͷ߲Λº͔Β⚪ʹมߋ͢Δ ʮೖྗ͢ΔʯΛԡͯ͠ɺԋशΛྃ͢Δ
λʔϛφϧͰ/PEFKTͷόʔδϣϯΛද͍ࣔͯͩ͘͠͞ɻ $ node -v v12.13.1 ԋशςετ ԋशϦϯΫ https://chouseisan.com/s?h=4a0d7eb9c38a440e9ad585abfbb16e79 ‣ bit.ly/2XWSQQK ͜ͷࢿྉͷϦϯΫ https://speakerdeck.com/ajido/minimum-handson-nodejs ! 6 / 53 -
ඪ४Ϟδϡʔϧʢ$PSF"1*ʣ /PEFKT(PPHMF$ISPNFʹ࠾༻͞Ε͍ͯΔ7+BWB4DSJQUΤϯδϯͱɺඪ४ͷ +BWB4DSJQUʹͳ͍֤छ࣮͕Έ߹ΘͤΒΕͨ+BWB4DSJQUͷ࣮ߦڥͰ͢ɻ const fs = require(‘fs') fs.readFile(__filename, (err, data)
=> { console.log(data) }) ͜͜Ͱͷඪ४Ϟδϡʔϧͱɺ/PEFKTʹΈࠐ·Εͨʮඪ४ͷ+BWB4DSJQUʹͳ͍ ֤छ࣮ʯͷ͜ͱΛࢦ͠·͢ɻ __filenameࣗͷεΫϦϓτ ϑΝΠϧͷઈରύεͰ͢ɻ ! 7 / 53 -
ඪ४Ϟδϡʔϧʢ$PSF"1*ʣ fs ϑΝΠϧΛ࡞ɾআ͢Δ path ϑΝΠϧύεɾσΟϨΫτϦύεΛѻ͏ http )551αʔόɾΫϥΠΞϯτΛ࣮͢Δ crypto ϋογϡɾ҉߸ɾॺ໊ɾݕূͳͲͷ҉߸ػೳΛѻ͏ util
ϢʔςΟϦςΟϞδϡʔϧ querystring 63-ΫΤϦจࣈྻΛύʔε͢Δ os $16ͷϗετ໊ͳͲͷ04ؔ࿈ใΛࢀর͢Δ child_process ࢠϓϩηεΛ࡞͢Δ cluster αʔόΛϚϧνϓϩηεԽ͢Δ assert มͷ༰ॲཧͷԠΛνΣοΫ͢Δ ΧςΰϦ جຊతͰଞʹґଘੑͷͳ͍"1* Events, Modules, Buffer, Stream ΧςΰϦ /PEFݻ༗ͷͷͰຊମͷಈ࡞ʹؔ࿈͢Δ"1* Timers, Process, Console ΧςΰϦ 04ͷػೳଞͷϥΠϒϥϦͱؔ࿈͢Δ"1* File System, Net, UDP/Datagram, TLS/SSL, Child Processes ΧςΰϦ ΞϓϦέʔγϣϯ͚ͷԠ༻"1* HTTP, HTTPS, Cluster IUUQTOPEFKTPSHEJTUMBUFTUEPDTBQJEPDVNFOUBUJPOIUNM ֤छඪ४Ϟδϡʔϧͱͦͷ༻్ͷҰྫ جఈΦϒδΣΫτผʹΧςΰϥΠζͨ͠ਤɻ ΧςΰϦ͔Βॱʹֶश͢Δ͜ͱͰɺΑΓཧղ͕ਂ·Γ·͢ ! 8 / 53 -
3&1- /PEFKTͷରతΠϯλʔϑΣʔεͰ͢ɻ3&1-ʢ3FBE&WBM1SJOU-PPQʣڥͰ ඪ४Ϟδϡʔϧ͕ࣗಈతʹϩʔυ͞ΕΔͨΊɺͦΕΒΛಡΈࠐΉ͜ͱͳ͘ѻ͑·͢ɻ $ node > /^\d+$/.test(‘1024’) true // requireすることなく標準モジュールを呼び出せます
> os.hostname() ‘MAC-Z-941.local’ > .exit ίʔυͷ͍͢ݕূʹཱͪ·͢ɻԋशதੋඇ׆༻ͯ͠Έ͍ͯͩ͘͞ɻ 3&1-ͷऴྃ<Ctrl>-D ΛૹΔ͔ .exit ͱೖྗ͍ͯͩ͘͠͞ɻ ͦͷଞίϚϯυ.helpΛࢀর ! 9 / 53 -
ࣗͷͷ$16ίΞͷΛɺඪ४Ϟδϡʔϧͱ3&1-Λͬͯද͍ࣔͯͩ͘͠͞ɻ $ node > 䡧䡧䡧䡧䡧䡧䡧䡧䡧 [ { model: 'Intel(R)
Core(TM) i5-5287U CPU @ 2.90GHz', speed: 2900, times: { user: 78882290, nice: 0, sys: 63998990, idle: 611174200, irq: 0 } }, ... ] > 䡧䡧䡧䡧䡧䡧䡧䡧䡧䡧䡧䡧䡧䡧䡧䡧 4 ԋश ͜ͷࢿྉͷϦϯΫ https://speakerdeck.com/ajido/minimum-handson-nodejs ԋशϦϯΫ https://chouseisan.com/s?h=4a0d7eb9c38a440e9ad585abfbb16e79 ‣ bit.ly/2XWSQQK εΫϦϓτϦϯΫ https://github.com/ajido/nodejs-handson 3&1-ͷऴྃ<Ctrl>-D ΛૹΔ͔ .exit ͱೖྗ͍ͯͩ͘͠͞ɻ ͦͷଞίϚϯυ.helpΛࢀর ! 10 / 53 -
OQN OQN /PEF1BDLBHF.BOBHFS OQN *ODʹΑΓཧɾఏڙ͞Ε͍ͯΔ/PEFKTΈ ࠐΈͷύοέʔδཧγεςϜͰ͢ɻੈքத͔Βར༻͞ΕΔதԝϨδετϦͷOQNKTDPNʹ ଟͷϞδϡʔϧ͕ެ։͞Ε͍ͯ·͢ɻ ༰ όʔδϣϯཧ node_modules
Ϟδϡʔϧͷ࣮ମɾιʔείʔυ package-lock.json Πϯετʔϧ͞ΕͨϞδϡʔϧͷπϦʔใ ✔ package.json ύοέʔδͷϝλσʔλ ϓϩδΣΫτʹґଘ͢ΔϞδϡʔϧͷཧσʔλͳͲ ✔ ۮൃతͳύοέʔδͷެ։Λ͙ͨΊʹ package.jsonʹprivate:trueΛهͯ͠ ͍ͩ͘͞ɻ ͜ͷΦϓγϣϯΛ༗ޮʹ͢Δ͜ͱͰɺຊདྷ package.jsonʹهड़͠ͳ͚ΕͳΒͳ͍֤छ ใΛলུ͢Δ͜ͱͰ͖·͢ɻ package-lock.json͕͋Εnode_modules σΟϨΫτϦ෮ݩͰ͖·͢ɻnode_modules όʔδϣϯཧͷର͔Β֎͍ͯͩ͘͠͞ɻ ! 12 / 53 // プロジェクトを新規作成する $ npm init $ npm install request $ ls ├── node_modules ├── package.json └── package-lock.json -
npminitίϚϯυͰpackage.jsonΛ࡞ͨ͋͠ͱrequestϞδϡʔϧΛΠϯε τʔϧ͍ͯͩ͘͠͞ɻͦͷޙrequestϞδϡʔϧΛͬͯ)551ϦΫΤετΛૹΓɺ εςʔλείʔυΛද͍ࣔͯͩ͘͠͞ɻ const request = require('request') request('https://www.yahoo.co.jp', (err,
res, body) => { console.log(res.statusCode) }) ԋश ԋशϦϯΫ https://chouseisan.com/s?h=4a0d7eb9c38a440e9ad585abfbb16e79 ‣ bit.ly/2XWSQQK εΫϦϓτϦϯΫ https://github.com/ajido/nodejs-handson $ npm init $ npm install request ! 13 / 53 -
ηϚϯςΟοΫόʔδϣχϯά ."+03 ޙํޓੑΛࣦ͏ͱ͖ʹΠϯΫϦϝϯτ͞ΕΔ ͦͷࡍ.*/03ͱ1"5$)Λʹ͢Δ .*/03 ޙํޓੑ͕͋ΓɺػೳΛՃͨ͠ͱ͖ʹΠϯΫϦϝϯτ͞ΕΔ ͦͷࡍ1"5$)Λʹ͢Δ 1"5$) ޙํޓੑ͕͋ΓɺόάΛमਖ਼ͨ͠ͱ͖ʹΠϯΫϦϝϯτ͞ΕΔ ?
."+03.*/031"5$) ?ه߸ύοέʔδͷόʔδϣϯൣғΛࢦఆ͓ͯ͠Γɺࠨଆͷ Ͱͳ͍Λݻఆ͢Δ͜ͱΛද͍ͯ͠·͢ɻ͜Εഁյతͳ มߋΛආ͚ͨ߹ཧతͳΞοϓσʔτΛߦ͏͜ͱΛҙຯ͠·͢ɻ OQNύοέʔδ͜ͷ༷ ʹ४ڌͨ͠όʔδϣϯཧ͕ ਪ͞Ε͍ͯ·͢ɻ ύοέʔδར༻ऀجຊతʹ ηϚϯςΟοΫόʔδϣχ ϯάΛ೦಄ʹύοέʔδΛ ཧ͍ͯͩ͘͠͞ɻ ! 14 / 53 -
ϞδϡʔϧཧϫʔΫϑϩʔ $ npm init // 必要なら private: true を追加 $
npm install <name> // ツリー情報から node_modules を再構築 $ npm ci $ npm audit $ npm audit fix $ npm outdated $ npm update <name> / npm install <name>@<version> ϓϩδΣΫτΛ৽ن࡞͢Δ߹ طଘͷϓϩδΣΫτʹՃΘΔ߹ ੬ऑੑνΣοΫ ύοέʔδͷߋ৽νΣοΫɾՃ ! 15 / 53 -
ಛͱҙ /PEFKTͷαʔόʔγϯάϧϓϩηεɾγϯάϧεϨουͰେྔͷϦΫΤετΛॲཧ͠·͢ɻ͜ ͷ؍͔Βɺ·ͣ04ͷϓϩηε͋ͨΓͷϑΝΠϧσΟεΫϦϓλͷ੍ݶʹҙ͍ͯͩ͘͠͞ɻ # 初期値では明らかに足りません $ ulimit -n 1024 …
[Service] LimitNOFILE=65536 ॳظͷ··ϓϩμΫγϣϯڥͰϦΫΤετΛड͚͚ͯ͠·͏ͱɺߴ͍τϥϑΟοΫΛॲཧ ࢝͠ΊͨࡍʹToo many open filesʹΑΔϓϩηεμϯ͕΄΅࣮֬ʹൃੜ͠·͢ɻ $ ulimit -n 65536 $ ulimit -n 65536 ulimit -nʹΑΔ੍ݶͷ্ॻ͖ TZTUFNEʹΑΔ੍ݶͷઃఆ ัଊϚϧνίΞ͕׆༻Ͱ͖ΔڥͰҰൠ తʹclusterϞδϡʔϧΛͬͯϚϧνϓ ϩηεԽ͠·͢ɻ ! 17 / 53 -
Πϕϯτϧʔϓ /PEFKT͕େྔͷτϥϑΟοΫΛॲཧ͠ଓ͚ΔڥΛҡ࣋͢ΔͨΊʹɺΠϕϯτϧʔϓΛՄೳ ͳݶΓࢭΊͣʹӡ༻͢Δ͜ͱ͕ͬͱॏཁͰ͢ɻ͜ͷΠϕϯτϧʔϓ͕ͳʹ͔Λઆ໌͢Δલ ʹɺ·ͣΠϕϯτϧʔϓ͕؆୯ʹࢭ·ͬͯ͠·͏࣮ྫΛྻڍ͠·͢ɻ ϑΝΠϧωοτϫʔΫॲཧΛΘͳ͍$16Λ࣌ؒ༗͠ଓ͚Δॲཧͱɺػ࣌ؒͷ͍ಉظతͳ "1*Λར༻͢Δ͜ͱͰɺΠϕϯτϧʔϓக໋తͳ·Ͱʹఀࢭ͠·͢ɻ ͋Γ͕ͪ ΠϕϯτϧʔϓΛ࣌ؒఀࢭͤ͞Δ࣮ͷҰྫ ⚠⚠⚠ ඦ,#Ҏ্ͷڊେͳ+40/σʔλΛॲཧ͢Δ
⚠⚠ GTϞδϡʔϧͷಉظؔΛ͏ ⚠ ϧʔϓΛճ͠ଓ͚Δ ܦݧ্ɺڊେͳ+40/ͷύʔεʹΑΔ ύϑΥʔϚϯεྼԽ͕ඇৗʹଟ͍ɻ ! 18 / 53 -
Πϕϯτϧʔϓ const http = require('http') http.createServer((req, res) => { //
この部分の記述次第でパフォーマンスが劇的に変化する res.end() }).listen(8080) Πϕϯτϧʔϓ/PEFKTʹ͓͚Δ಄ͷΑ͏ͳͷͰɺ༩͑ΒΕ໋ͨྩΛॱ࣮࣍ߦ͢ ΔίΞͰ͢ɻ͜ΕΛҰఀࢭͤ͞ͳ͍ͱ͍͏͜ͱෆՄೳͰ͕͢ɺఀࢭ࣌ؒΛஅଓత͔ ͭ࠷খݶʹ͑ଓ͚Δ͜ͱͰɺ/PEFKTεϜʔζʹϦΫΤετΛॲཧ͠ଓ͚Δ͜ͱ͕ Ͱ͖·͢ɻ $ vegeta attack -duration=10s -rate=0 -max-workers=100 ॲཧͷ༰ 314 ͳ͠ JSON.parse 20KB JSON.parse 500KB fs.readFile 1MB fs.readFileSync 1MB forEach Loop 100 forEach Loop 100,000 ! 19 / 53 -
+40/σʔλͷ࠷దͳαΠζʁ ୯७ͳߏͷ+40/σʔλΛύʔεͨ͠ͱ͖ͷΠϕϯτϧʔϓͷఀࢭ࣌ؒ 0ms 50ms 100ms 150ms 200ms 100KB 300KB 500KB
700KB 900KB 2M 4M 6M 8M 10M 30M 50M ࠷దͳ༩͑ΒΕΔσʔλΞϓϦέʔγϣϯʹԠ࣮ͨ͡ࢪࢼݧʹΑׂͬͯΓग़͖͢Ͱ͕͢ɺ ҆ͱͯ͠ʮʢΑ΄Ͳେ͖ͯ͘ʣ࠷େ.ʹऩ·Δʯ͜ͱΛ೦಄ʹʮ࣌ؒܦաγεςϜͷʹ ͍+40/ͷαΠζ͕ංେԽ͠ͳ͍ʯ͜ͱͱʮϚϧνϓϩηεԽ͢Δʯ͜ͱʹཹҙ͍ͯͩ͘͠͞ɻ ! 20 / 53 ‑ڐ༰ൣғ ‑ݶք -
ίʔϧόοΫ ԼهͲͪΒϑΝΠϧΛಡΈࠐΈग़ྗ͍ͯ͠·͢ɻϑΝΠϧͷಡΈࠐΈʹfs.readFileSyncΛར ༻ͨ͠߹ಉظతͳॲཧͱͳΓɺಡΈࠐΈྃ·ͰΠϕϯτϧʔϓΛࢭΊͯ͠·͍·͕͢ɺ fs.readFileΛར༻͢ΔͱඇಉظతͳॲཧͱͳΓɺಡΈࠐΈྃ·ͰͷؒɺଞͷॲཧʹϦιʔεΛ ׂΓͯΔ͜ͱ͕Ͱ͖·͢ɻ // 非同期 … 読み込み中イベントループが停止しない。完了後にコールバック関数が呼び出される const
fs = require(‘fs') fs.readFile(__filename, (err, data) => { console.log(data.toString()) }) // 同期 … ファイル読み込み中イベントループが停止する const fs = require(‘fs') const data = fs.readFileSync(__filename).toString() console.log(data) ! 24 / 53 -
ίʔϧόοΫͷ׳ྫ ίʔϧόοΫؔجຊతʹෳճݺͼग़͞ΕΔ͜ͱ͋Γ·ͤΜɻྫ֎͋Γ·͕͢ɺඇ ಉظॲཧͷྃޙʹҰ͚ͩݺͼग़͞Ε·͢ɻͦͯ͠ݺͼग़͞ΕͨίʔϧόοΫؔͷୈҰ Ҿʹɺॲཧ͕ޭ͔ͨ͠Ͳ͏͔Λද͢ΤϥʔΦϒδΣΫτ͕ฦΓ·͢ɻ ίʔϧόοΫύλʔϯͷ׳ྫ "1*ͷ࠷ޙͷҾ͕ίʔϧόοΫؔ fs.readFile(path[, options], callback) ॲཧͷྃޙʹίʔϧόοΫ͕ؔҰ͚ͩݺͼग़͞ΕΔ
ݺͼग़͞ΕͨίʔϧόοΫؔͷୈҰҾ͕ඇಉظॲཧͷࣦഊΛද͢ΤϥʔΦϒδΣΫτ fs.readFile(__filename, (err, data) => { console.log(data.toString()) }) ! 25 / 53 -
ίʔϧόοΫͷΤϥʔϋϯυϦϯά ίʔϧόοΫؔͷୈҰҾ͕ΤϥʔΦϒδΣΫτͰ͢ɻΤϥʔϋϯυϦϯάୈҰҾ ͷΤϥʔΦϒδΣΫτͷOVMMνΣοΫΛߦ͍ɺOVMMͰͳ͍߹ΤϥʔΛॲཧͯ͘͠ ͍ͩ͞ɻ const fs = require('fs') fs.readFile(__filename, (err,
data) => { if (err) { console.error(err) return } console.log(data) }) Τϥʔͷதʹແࢹ͢ΔʢʹϋϯυϦϯά ͠ͳ͍ʣ͜ͱͰϓϩηε͕Ϋϥογϡͯ͠ ͠·͏ͷଘࡏ͠·͕͢ɺίʔϧόοΫ ୈҰҾͷΤϥʔແࢹͯ͠ϓϩηε͕ Ϋϥογϡ͢Δ͜ͱ͋Γ·ͤΜɻ ! 26 / 53 -
ίʔϧόοΫͷΑ͋͘Δࣦഊྫ returnͷॻ͖Ε // 誤 const fs = require('fs') fs.readFile(__filename, (err,
data) => { if (err) console.error(err) console.log(data) }) // 正 const fs = require('fs'); fs.readFile(__filename, (err, data) => { if (err) return console.error(err) console.log(data) }) ! 27 / 53 ҟৗܥͷॲཧͷ͋ͱਖ਼ৗܥͷॲཧ ͬͯ͠·͍·͢ɻ -
ίʔϧόοΫͷΑ͋͘Δࣦഊྫ try/catchΛͬͯඇಉظॲཧͷΤϥʔΛั·͑Δ // 誤 const fs = require('fs') try {
fs.readFile(__filename, (err, data) => { console.log(data) }) } catch (err) { console.error(err) } // 正 const fs = require('fs') fs.readFile(__filename, (err, data) => { if (err) return console.error(err) console.log(data) }) ͨͩ͠ඇಉظॲཧͷલஈ֊ͷΤϥʔ͜ͷtry/catchʹΑͬͯิͰ͖·͢ɻ ͦͷͨΊ͜ͷɹ try/catchݫີʹඞཁͱݴ͑Δ͜ͱʹҙ͍ͯͩ͘͠͞ɻ ! 28 / 53 -
ίʔϧόοΫͷΑ͋͘Δࣦഊྫ ඇಉظॲཧͷಉظతͳϧʔϓ // 誤 const fs = require('fs') for (let
i = 0; i < 100; i++) { fs.appendFile('./data.txt', i + ',', (err) => { }) } // 0,3,4,5,6,7,8,9,10,11,12,20,21,1,2,28,3,4,5,6,7,8... // 再帰処理による対処の一例(必ずしも正解というわけではありません) const write = (i) => { if (i === 100) return fs.appendFile('./data.txt', i + ',', (err) => { write(++i) }) } write(0) 0-99ͷΛॱʹϑΝΠϧʹॻ͖ࠐΉ͜ͱΛҙ ਤͯ͠ϧʔϓ͍ͯ͠·͕͢ɺ࣮ࡍʮ΄΅ಉ࣌ʹ 0-99ͷΛϑΝΠϧʹॻ͖ࠐΉʯॲཧʹͳͬ ͍ͯ·͢ɻ͜ͷͨΊ݁Ռॱෆಉͳͷฒͼʹ ͳΓ·͢ɻ ! 29 / 53 -
Πϕϯτۦಈ ඇಉظॲཧͱͷੑ͕ߴ͍͏ͻͱͭͷσβΠϯύ λʔϯ͕ΠϕϯτۦಈͰ͢ɻ/PEFKTͷΠϕϯτۦ ಈύλʔϯEventEmitterͱ͍͏جఈΫϥεΛ ҙͷΦϒδΣΫτʹܧঝͤ͞Δ͜ͱͰ࣮͠·͢ɻ const Redis = require('ioredis') const
redis = new Redis() const stream = redis.scanStream({ match: '*', count: 100 }) // 約100件のキーを読み込むごとに呼び出される stream.on('data', (keys) => { for (const key of keys) { console.log(key) } }) // 全てのキーを走査した後に呼び出される stream.on('end', () => { console.log(‘end') }) ,74ʹଓͯ͠ΩʔͷҰཡΛྻڍ͢ΔΠϕϯτۦಈܕͷίʔυ EventEmitter Λܧঝͨ͠ ΠϕϯτۦಈܕͷΦϒδΣΫτ ίʔϧόοΫͱͷҧ͍ ॲཧͷྃʹݶΒͣɺॲཧͷ్தɾ։࢝ɾऴྃͳͲɺ ঢ়ଶͷมԽΛද༷͢ʑͳλΠϛϯάͰॲཧΛڬΈࠐΉ͜ͱ͕Ͱ͖Δ ঢ়ଶͷมԽΛද͢ʮΠϕϯτʯԿ܁Γฦ͠ൃੜ͠ɺ ͦͷʹରԠ͢ΔϦεφʔ͕ؔݺͼग़͞ΕΔ ,74ʹ͑Εͳ͍ྔͷΩʔ͕ଘࡏ͍ͯͨ͠ͱͯ͠ɺ݅୯ҐͰॲཧ ͍ͯ͠ΔͨΊɺϝϞϦΛѹഭ͢Δ͜ͱͳ͘ɺΠϕϯτϧʔϓΛ࣌ؒఀࢭ͞ ͤΔ͜ͱ͋Γ·ͤΜɻ ! 31 / 53 -
ΠϕϯτۦಈͷΤϥʔϋϯυϦϯά ΠϕϯτۦಈܕͷΠϯλʔϑΣʔεΛ࣋ͬͨΦϒδΣ ΫτͷΤϥʔΠϕϯτʹϦεφʔؔΛՃ͠·͢ɻ const Redis = require('ioredis') const redis =
new Redis() const stream = redis.scanStream({ match: '*', count: 100 }) // 約100件のキーを読み込むごとに呼び出される stream.on('data', (keys) => { for (const key of keys) { console.log(key) } }) // 全てのキーを走査した後に呼び出される stream.on('end', () => { console.log(‘end') }) // 何かしらエラーが発生した時に呼び出される stream.on('error', (err) => { console.error(err) }) ΠϕϯτۦಈܕͷΦϒδΣΫτΛݟ͚Δώϯτ ˞ඞཱͣ͢͠Δ݅Ͱ͋Γ·ͤΜ υΩϡϝϯτʹൃੜ͢ΔΠϕϯτ͕هࡌ͞Ε͍ͯΔ .onϝιου͕ଘࡏ͢Δ streamͱ͍͏໊শ͕ΘΕ͍ͯΔ ΦϒδΣΫτ͕Πϕϯτۦಈܕ͔Ͳ͏͔ɺओʹυΩϡϝϯτ͔Βஅ͠· ͢ɻͦͯ͠ΠϕϯτۦಈܕͷΠϯλʔϑΣʔεͰ͋ͬͨ߹on(error) ΛͬͯΤϥʔॲཧΛՃ͍ͯͩ͘͠͞ɻ emitter.on('error', (err) => { console.error(err) }) ! 32 / 53 -
ΠϕϯτۦಈͷΑ͋͘Δࣦഊྫ on('error')ͷϋϯυϦϯά࿙ΕͱͦΕʹ͏ϓϩηεͷΫϥογϡ // 誤 const fs = require('fs') const rs
= fs.createReadStream(__filename) rs.on('data', (data) => { console.log(data) }) // 正 const fs = require('fs') const rs = fs.createReadStream(__filename) rs.on('data', (data) => { console.log(data) }) rs.on('error', (err) => { console.error(err) }) ΠϕϯτۦಈܕͷΤϥʔϋϯυϦϯά୭͕Ε͕ͪͰ͢ɻϋϯυϦϯάΛΕΔͱΤϥʔ ൃੜ࣌ʹϓϩηε͕Ϋϥογϡͯ͠͠·͏ͨΊɺࡉ৺ͷҙΛ͏ඞཁ͕͋Γ·͢ɻ ! 33 / 53 -
ಉظॲཧͱඇಉظॲཧΛݟ͚Δʹʁ const data = Buffer.alloc(1024 * 1024 * 1024) data.forEach((value)
=> { console.log(value) }) ͜Ε·Ͱඇಉظॲཧʹదͨ͠σβΠϯύλʔϯͱɺͦΕʹରԠ͢ΔΤϥʔϋϯυϦϯάΛղઆ ͠·͕ͨ͠ɺ࠷ޙʹಉظॲཧʹ͍ͭͯհ͠·͢ɻ·ͣಉظॲཧͱඇಉظॲཧΛݟ͚Δํ๏ Ͱ͕͢ɺද໘తͳใ͔Β࣮֬ʹஅ͢Δํ๏͋Γ·ͤΜɻͰ͕͢ҎԼʹώϯτͱͳΔใ Λྻڍ͠·͢ɻ ඇಉظॲཧΛݟ͚Δώϯτ˞ඞཱͣ͢͠Δ݅Ͱ͋Γ·ͤΜ ωοτϫʔΫ·ͨϑΝΠϧΛѻ͏ "1*ͷ࠷ޙͷҾ͕callbackͰ͋Γɺ"1*ͷ໊শʹSync͕͍͍ͯͳ͍ 1SPNJTFΦϒδΣΫτΛฦ͢ ! 35 / 53 ίʔϧόοΫؔΛड͚औΔ"1*Ͱ͕͢ ಉظॲཧͰ͢ɻίʔϧόοΫʹඇಉظॲ ཧͱ͍͏Θ͚Ͱ͋Γ·ͤΜ -
ಉظॲཧͷΤϥʔϋϯυϦϯά ಉظॲཧ্͔ΒॱʹίʔυΛ࣮ߦͯ͠ɺ࣮ߦதͷॲཧ͕ྃ͢Δ·Ͱ࣍ͷॲཧͷ։࢝ ΛϒϩοΫ͢ΔҰൠతͰײతͳૢ࡞Ͱ͢ɻ͜ͷಉظॲཧͷΤϥʔϋϯυϦϯάɺಉ ظॲཧΛtry/catchͰғ͏͜ͱʹΑͬͯߦ͍·͢ɻ try { // JSON.parseの例外処理は特に忘れがちなので注意してください const data
= JSON.parse('invalid_json') } catch (err) { console.error(err) } try { const data = fs.readFileSync('invalid_path') } catch (err) { console.error(err) } fs.readFileSyncthrownewError()try/ catchΛͬͯΤϥʔΛॲཧ͠·͢ɻಉظॲཧͷΤϥʔ ϋϯυϦϯά͕࿙Ε͍ͯΔͱϓϩηε͕Ϋϥογϡ͢Δͨ Ίɺेʹҙ͍ͯͩ͘͠͞ɻ ! 36 / 53 -
νΣοΫϙΠϯτ ‣ /PEFKT-54όʔδϣϯΛར༻͢Δ ‣ Ϟδϡʔϧͷར༻ͱཧํ๏Λཧղ͢Δ ‣ ඇಉظॲཧΛۦͯ͠Πϕϯτϧʔϓͷఀࢭ࣌ؒΛ࠷খݶʹ͑Δ ‣ ίʔϧόοΫͱΠϕϯτۦಈͷσβΠϯΛཧղ͢Δ ‣
ίʔϧόοΫͱΠϕϯτۦಈͷΤϥʔϋϯυϦϯάํ๏Λཧղ͢Δ ‣ ಉظॲཧͷΤϥʔϋϯυϦϯάํ๏Λཧղ͢Δ ઃܭύλʔϯ ΤϥʔϋϯυϦϯά Ϋϥογϡ ඇಉظॲཧ $BMMCBDL if (err) &WFOU&NJUUFS emitter.on(‘error') ✔ ಉظॲཧ try/catch ✔ ! 37 / 53 -
ϑϩʔ੍ޚ app.get(‘/upload', (req, res) => { // 1. データベースに問い合わせてユーザー情報を取得する非同期処理 //
2. オブジェクトストレージにファイルをアップロードする非同期処理 // 3. データベースにメタ情報を保存する非同期処理 res.status(200).end(data) } 8FC"1*ͷ։ൃͰԼهͷΑ͏ʹྻͷॲཧʢલͷॲཧ͕ऴ͔ྃͯ͠Β࣍ͷॲཧΛߦ͏ʣΛه ड़͢Δ͜ͱ͕΄ͱΜͲͰ͕͢ɺඇಉظॲཧͱྻॲཧͷ૬ੑ࠷ѱͰ͢ɻ͔͠͠ݱࡏͰ ྻॲཧͷهड़Λվળ͢ΔݴޠϨϕϧͷ༷͕උΘ͍ͬͯ·͢ɻ ! 39 / 53 -
ϑϩʔ੍ޚֶशͷྲྀΕ ֶशॱ ઃܭύλʔϯ ޓੑ ϑϩʔ੍ޚ ετϦʔϜॲཧ هड़ྔ $BMMCBDL ✔
&WFOU&NJUUFS ✔ 1SPNJTF ✔ ✔ BTZODBXBJU ✔ ✔ ✔ جૅͷσβΠϯύλʔϯʹΑΔϑϩʔ੍ޚΛɺΑΓ্Ґͷϑϩʔ੍ޚʹม͢Δ͜ͱͰֶशΛਐ Ί͍͖ͯ·͢ɻࠓճதؒͰ͋ΔPromiseΛεΩοϓͯ͠ɺ࠷ऴతͳணͰ͋Δasync/ awaitΛղઆ͠·͢ɻ ! 40 / 53 ֤ઃܭύλʔϯͷ্ҐޓͰ͋Δasync/awaitΛղઆ͠·͢ɻ -
ίʔϧόοΫʹΑΔϑϩʔ੍ޚ ίʔϧόοΫΛͬͨϑϩʔ੍ޚ༰қʹཧղͰ͖ΔγϯϓϧͳΈͰ͋Γ·͢ ͕ɺॲཧͷ༰࣍ୈͰਂ͍ωετͱ࠶ىॲཧʹΑΓɺෳࡶ͕͞૿͠ՄಡੑͷѱԽͱه ड़ྔͷංେԽʹܨ͕Γ·͢ɻ // 再帰処理による非同期処理のループ const fs = require('fs')
const write = (i) => { if (i === 100) return fs.appendFile('./data.txt', `${i},`, (err) => { write(++i) }) } write(0) ϑΝΠϧͷॻ͖ࠐΈ͕ྃͨ͋͠ͱ࣍ͷॲཧΛ։࢝͢Δ ͱ͍͏ྻॲཧͰ͕͢ɺඇಉظॲཧͷͨΊϑΝΠϧॻ͖ࠐ ΈྃͷίʔϧόοΫΛىʹ࠶ؼॲཧ͍ͯ͠·͢ɻ ! 41 / 53 -
BTZODBXBJU const fs = require('fs').promises const main = async ()
=> { for (let i = 0; i < 100; i++) { await fs.appendFile('./data.txt', `${i},`) } } main() const fs = require('fs') const write = (i) => { if (i === 100) return fs.appendFile('./data.txt', `${i},`, (err) => { write(++i) }) } write(0) ίʔϧόοΫͷ࠶ؼॲཧΛasync/awaitʹม͠·͢ɻasync/awaitʹΑͬͯίʔ υελΠϧҰൠతͳಉظॲཧʹ͍ۙߏʹมԽ͠·͢ɻͰ͕͢ඇಉظॲཧͷϝϦοτ ଛͳΘΕ͓ͯΒͣɺॲཧͷ࣮ߦதଞͷॲཧʹϦιʔεΛׂΓͯΔ͜ͱ͕Ͱ͖·͢ɻ $BMMCBDL BTZODBXBJU const data = await func()ͷΑ͏ʹඇ ಉظॲཧͷฦΓΛड͚औΔ͜ͱͰ͖·͢ɻ ! 43 / 53 -
BTZODBXBJU const fs = require('fs').promises const main = async ()
=> { for (let i = 0; i < 100; i++) { await fs.appendFile('./data.txt', `${i},`) } } main() asyncfunctionએݴʹΑͬͯඇಉظؔʢ"TZOD'VOUJPOʣΛఆٛ͠·͢ʢ⁞ʣɻඇಉ ظؔͷ෦ͰawaitʹΑͬͯඇಉظॲཧͷྃΛػͰ͖·͢ʢ ʣɻ Promise4USVDUVSFEͳίʔϧόοΫͰ͢ɻ /PEFKTͷҰ෦ͷඪ४Ϟδϡʔϧ.promises Λ༩͢Δ͜ͱͰίʔϧόοΫͱಉ໊͡લͷ 1SPNJTF"1*Λฦ͠·͢ʢʣɻ ௨ৗͷίʔϧόοΫ"1*ͰawaitʹΑͬͯॲཧͷྃΛػͤ͞Δ͜ͱ͕Ͱ͖·ͤΜɻ awaitʹ͢ඇಉظॲཧಛఆͷϧʔϧʹैͬͨΦϒδΣΫτͰ͋Δඞཁ͕͋Γ·͢ɻ ⁞ ! 44 / 53 -
ԼهίʔϧόοΫύλʔϯͷ࠶ؼॲཧΛasync/awaitΛͬͨॲཧʹม͍ͯͩ͘͠͞ɻ ԋश const fs = require('fs') const write =
(i) => { if (i === 100) return fs.appendFile('./data.txt', `${i},`, (err) => { write(++i) }) } write(0) ͜ͷࢿྉͷϦϯΫ https://speakerdeck.com/ajido/minimum-handson-nodejs ԋशϦϯΫ https://chouseisan.com/s?h=4a0d7eb9c38a440e9ad585abfbb16e79 ‣ bit.ly/2XWSQQK εΫϦϓτϦϯΫ https://github.com/ajido/nodejs-handson ! 45 / 53 -
Πϕϯτۦಈύλʔϯͷϑϩʔ੍ޚ try { for await (const keys of stream) {
for (const key of keys) { console.log(key) } } } catch (err) { console.error(err) } ΠϕϯτۦಈܕͷॲཧforawaitߏจʹΑΓ "TZOD'VODUJPOͷϑϩʔ੍ޚԼʹΈࠐΉ͜ ͱ͕Ͱ͖·͢ɻ const Redis = require('ioredis') const redis = new Redis() const stream = redis.scanStream({ match: '*', count: 100 }) // 約100件のキーを読み込むごとに呼び出される stream.on('data', (keys) => { for (const key of keys) { console.log(key) } }) // 全てのキーを走査した後に呼び出される stream.on('end', () => { console.log(‘end') }) // 何かしらエラーが発生した時に呼び出される stream.on('error', (err) => { console.error(err) }) ίʔϧόοΫ͕PromiseʹΑͬͯߏԽ͞Ε͍ͯͨΑ͏ ʹɺΠϕϯτۦಈܕͷॲཧstream.ReadableʹΑͬͯ ߏԽ͞Ε͍ͯΔඞཁ͕͋Γ·͢ɻ ! 46 / 53 -
ԼهΠϕϯτۦಈύλʔϯͷίʔυΛforawaitΛͬͨॲཧʹม͍ͯͩ͘͠͞ɻ ԋश const fs = require('fs') const rs =
fs.createReadStream(__filename) rs.on('data', (data) => { console.log(data.toString()) }) rs.on('error', (err) => { console.error(err) }) ͜ͷࢿྉͷϦϯΫ https://speakerdeck.com/ajido/minimum-handson-nodejs ԋशϦϯΫ https://chouseisan.com/s?h=4a0d7eb9c38a440e9ad585abfbb16e79 ‣ bit.ly/2XWSQQK εΫϦϓτϦϯΫ https://github.com/ajido/nodejs-handson ! 47 / 53 -
BTZODBXBJUͷΤϥʔϋϯυϦϯά "TZOD'VODUJPOͷଆͰtry/catchΛʢ⁞ʣɺ"TZOD'VODUJPOͷ֎ଆͰ .catchΛͬͯΤϥʔΛॲཧ͍ͯͩ͘͠͞ʢ ʣɻ const fs = require('fs').promises const main =
async () => { for (let i = 0; i < 100; i++) { try { await fs.appendFile('./data.txt', `${i},`) } catch (err) { console.error(err) } } } main().catch((err) => { console.error(err) }) ⁞ "TZOD'VODUJPOͷ෦ͰΤϥʔ͕ൃੜ͢ ΔͱɺҎ߱ͷॲཧ࣮ߦ͞Εͣ.catchʹ δϟϯϓ͠·͢ɻ ͨͩ͠"TZOD'VODUJPO෦ͷΤϥʔൃੜ ෦͕try/catchͰแ·Ε͍ͯΔ߹ɺ try/catchʹัଊ͞Ε.catchʹಧ͖· ͤΜɻ͜ͷ߹try/catchΛൈ͚ͨޙ "TZOD'VODUJPOͷॲཧܧଓ͞Ε·͢ɻ ! 48 / 53 -
BTZODBXBJUͷΑ͋͘Δࣦഊྫ ⁞awaitͷॻ͖Ε ෆཁͳPromiseͷར༻ .catchͷॻ͖ΕʢΤϥʔϋϯυϦϯάͷهड़࿙Εʣ const fs = require('fs').promises const main
= async () => { for (let i = 0; i < 100; i++) { const stat = fs.appendFile('./data.txt', `${i},`).then(() => { return fs.stat(‘./data.txt’) }) // await fs.appendFile('./data.txt', `${i},`) console.log(stat) // const stat = await fs.stat('./data.txt') } return Promise.resolve(‘done') // return 'done' } main().then((result) => console.log(result)) // .catch((err) => console.error(err)) ⁞ ! 49 / 53 -
BTZODBXBJUΛͬͨแׅతͳΤϥʔϋϯυϦϯά BTZODBXBJU ݫີʹ1SPNJTF try/catchͷׂΛ݉Ͷἧ͍͑ͯ·͢ɻԼهίʔυͰ "TZOD'VODUJPOͷଆͰൃੜ͢ΔಉظॲཧͷΤϥʔΛ.catchʹٵऩ͍ͤͯ͞·͢ɻॲཧΛ͢ ͯasync/awaitڥԼʹ࣮͢Δ͜ͱͰɺඇಉظɾಉظॲཧΛΘͣɺแׅతʹΤϥʔΛॲཧ Ͱ͖·͢ɻ const main =
async () => { JSON.parse('invalid_json') } main().catch((err) => { console.error(err) }) // SyntaxError: // Unexpected token b in JSON at position 0 // at JSON.parse (<anonymous>) try/catchͷهड़࿙ΕʹΑΔϓϩηεμϯΛ͗·͢ɻͯ͢ͷ έʔεʹ͓͍ͯasync/awaitΛར༻͢Δͱ͍͏ϧʔϧҰݟۃ ʹࢥ͑Δ͔͠Ε·ͤΜ͕ɺՄಡੑͷ໘Ͱ҆શੑͷ໘Ͱ͓͢͢Ί Ͱ͖Δ༏Εͨํ๏Ͱ͢ɻ ! 50 / 53 -
νΣοΫϙΠϯτ ‣ BTZODBXBJUͷѻ͍ํΛཧղ͢Δ ‣ BTZODBXBJUͷΤϥʔϋϯυϦϯάํ๏Λཧղ͢Δ ‣ Մಡੑͱ҆શੑΛߟྀ͠ɺՄೳͳݶΓBTZODBXBJUϑΝʔετͷઃܭΛ৺ֻ͚Δ ઃܭύλʔϯ ΤϥʔϋϯυϦϯά Ϋϥογϡ
ඇಉظॲཧ $BMMCBDL if (err) &WFOU&NJUUFS emitter.on(‘error') ✔ BTZODBXBJU try/catch .catch() ಉظॲཧ try/catch BTZODBXBJU ✔ ! 51 / 53 -
جૅ Ԡ༻ ‣ Πϯετʔϧ 5 ‣ ϑϩʔ੍ޚ 38 ‣ Ϟδϡʔϧཧ
11 ‣ ඇಉظؔ 42 ‣ ಛ 16 ‣ σόοά ‣ σβΠϯύλʔϯ 21 ‣ Ϟδϡʔϧͷ࡞ ‣ ίʔϧόοΫ 23 ‣ ϓϩϑΝΠϦϯά ‣ Πϕϯτۦಈ 31 ‣ ϑϨʔϜϫʔΫͱπʔϧ ‣ ಉظॲཧ 35