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

Callback to Promise and beyond

Callback to Promise and beyond

Daiki Kuriyama

February 01, 2019
Tweet

More Decks by Daiki Kuriyama

Other Decks in Technology

Transcript

  1. $BMMCBDLUP1SPNJTFBOECFZPOE
    d1SPNJTFԽ͚ͩͰ͸ऴΘΒͳ͍d
    ೥݄೔ؔ੢/PEFֶԂ࣌ݶ໨
    Ϡϑʔגࣜձࣾɹ܀ࢁଠر

    View Slide

  2. ࣗݾ঺հ
    ܀ࢁଠرʢ!"KJEPʣ
    Ϡϑʔגࣜձࣾ›/PEFKTࠇଳ

    View Slide

  3. w $BMMCBDLΛ1SPNJTFԽ͢Δํ๏
    w $BMMCBDLΛ1SPNJTFԽͨ͠ޙʹ࢒Δ՝୊
    w ੑೳ›ݱ୅తͳϑϩʔ੍ޚ͸ߴ଎ͳͷ͔ʁ
    w ετϦʔϜରԠ›4USFBNΛϑϩʔ੍ޚʹͲ͏૊ΈࠐΉʁ
    w Τϥʔॲཧ›ඇಉظॲཧͷελοΫτϨʔε͸Ͳ͏ݟ͑Δʁ
    ಺༰

    View Slide

  4. /PEFKTʹ͓͚Δඇಉظॲཧͱϑϩʔ੍ޚͷྺ࢙
    "TZODUZQF /PEFWFSTJPO
    $BMMCBDL
    3FNPWFQSPNJTFT
    W
    BTZOD OQN

    CMVFCJSE OQN
    2 OQN

    1SPNJTF" W 7

    (FOFSBUPS W 7

    "TZOD'VODUJPOT BTZODBXBJU
    W 7

    "TZOD*UFSBUJPO GPSBXBJUPG
    &YQFSJNFOUBM

    View Slide

  5. BTZODBXBJUʹΑͬͯඇಉظॲཧͷϑϩʔ੍ޚ͸༰қʹͳ͕ͬͨɺ$BMMCBDLܕͷϝ
    ιου͸1SPNJTFʹม׵͠ͳ͚Ε͹ϑϩʔ੍ޚʹ૊ΈࠐΉ͜ͱ͕Ͱ͖ͳ͍ɻ1SPNJTF
    ʹରԠ͍ͯ͠ͳ͍ɺϑϩʔ੍ޚʹ૊Έࠐ·ΕΔͰ͋Ζ͏Ϟδϡʔϧ͸ҎԼͷ͍ͣΕ͔ͷ
    બ୒ΛഭΒΕͨɻ
    $BMMCBDLΛݱ୅తͳϑϩʔ੍ޚʹରԠͤ͞Δ
    $BMMCBDLͷ··ܧଓ͢Δʢ1SPNJTFม׵͸ར༻ऀʹ೚ͤΔʣ
    1SPNJTFʹ੾Γସ͑ɺޓ׵ੑΛࣺͯΔ
    $BMMCBDLͱ1SPNJTFͷΠϯλʔϑΣʔεΛཱ྆ͤ͞Δ
    બ୒

    View Slide

  6. $BMMCBDLͱ1SPNJTFͷΠϯλʔϑΣʔεΛཱ྆ͤ͞Δ
    $BMMCBDLΛলུͨ࣌͠ʹ1SPNJTFΛฦ͢
    1SPNJTFઐ༻ͷΠϯλʔϑΣʔεΛ௥Ճ͢Δ
    ‣ 1SPNJTFܕͷϞδϡʔϧΛฦ͢
    ‣ ϝιουνΣʔϯ͔Β1SPNJTFΛฦ͢
    ‣ 4VGpY෇͖ͷؔ਺͔Β1SPNJTFΛฦ͢
    Ϟδϡʔϧ಺Ͱ$BMMCBDLͱ1SPNJTFͷΠϯλʔϑΣʔεΛཱ྆ͤ͞Δํ๏ʹ͸͍ͭ͘
    ͔ͷબ୒ࢶ͕͋ΓɺͦΕͧΕʹϝϦοτͱσϝϦοτ͕ଘࡏ͢Δɻ

    View Slide

  7. $BMMCBDLͱ1SPNJTFͷཱ྆
    $BMMCBDLΛলུͨ࣌͠ʹ1SPNJTF͕ฦΔ
    // Callback
    func((data) => {
    console.log(data)
    })
    // Promise
    const data = await func()
    console.log(data)
    ϚʔέοτΛௐࠪ͢ΔݶΓ͜ͷελΠϧͷ࠾༻ྫ͸ଟ͘ɺ+BWB4DSJQUͷจԽΛݟͯ΋
    ύϥϝʔλΛলུͯ͠ڍಈ͕มΘΔ࢓૊Έ͸ଟ͘ଘࡏ͍ͯ͠Δɻ
    ϝιου͝ͱͷஈ֊తͳ1SPNJTFԽ͕ՄೳͰɺকདྷతͳ$BMMCBDLͷഇࢭ΋ࢹ໺ʹೖ
    Ε΍͍͢ɻͨͩ͠TFU5JNFPVU΍DIJME@QSPDFTTFYFDͷΑ͏ͳίʔϧόοΫؔ
    ਺Λड͚औΓɺಉ࣌ʹฦΓ஋΋ଘࡏ͢Δؔ਺ͱ͸૬ੑ͕ѱ͍ͨΊ஫ҙ͕ඞཁɻ

    View Slide

  8. $BMMCBDLͱ1SPNJTFͷཱ྆
    1SPNJTFܕͷϞδϡʔϧΛฦ͢
    // Callback
    const fs = require('fs')
    fs.readFile('/etc/hosts', (err, data) => {
    console.log(data)
    })
    // Promise
    const fs = require('fs').promises
    const data = await fs.readFile('/etc/hosts')
    console.log(data)
    /PEFKTͷຊମ͕࠾༻͍ͯ͠Δํ๏ɻ͢΂ͯͷϝιουΛҰׅͰ1SPNJTFԽͰ͖Δͱ
    ͍͏ར఺͕͋Γɺ·ͨϞδϡʔϧͷ։ൃऀࢹ఺Ͱ͸طଘͷ$BMMCBDLؔ਺ʹखΛՃ͑Δ
    ඞཁ͕ͳ͍ͱ͍͏ར఺΋͋Δɻ
    ͔͠͠Ϟδϡʔϧ಺ʹଘࡏ͢Δ͢΂ͯͷཁૉΛෳ੡ͯ͠վम͢Δඞཁ͕͋ΔͨΊɺରԠ
    ͷίετ͸վम࣌ʹूத͢Δɻ

    View Slide

  9. $BMMCBDLͱ1SPNJTFͷཱ྆
    ϝιουνΣʔϯ͔Β1SPNJTFΛฦ͢
    // Callback
    func((data) => {
    console.log(data)
    })
    // Promise
    const data = await func().promise()
    console.log(data)
    "844%,GPS+BWB4DSJQUͳͲ͕࠾༻͍ͯ͠Δํ๏ɻ1SPNJTFʹݶΒͣɺ4USFBN
    ͷΑ͏ͳෳ਺ύλʔϯͷΠϯλʔϑΣʔεΛ໌ࣔతʹࢦఆͯ͠ฦ͢͜ͱ΋Ͱ͖Δɻ
    طଘͷ$BMMCBDLؔ਺ʹखΛՃ͑Δඞཁ͕ͳ͍ͱ͍͏ར఺͕͋ΓɺϞδϡʔϧ಺ͷϝιο
    υΛ෦෼తʹ1SPNJTFରԠ͍ͯ͘͜͠ͱ΋Ͱ͖Δ͕ɺকདྷతͳ$BMMCBDLͷഇࢭΛࢹ
    ໺ʹೖΕΔ৔߹͸ѻ͍͕೉͍͠ɻ

    View Slide

  10. $BMMCBDLͱ1SPNJTFͷཱ྆
    4VGpY෇͖ͷؔ਺͔Β1SPNJTFΛฦ͢
    // Callback
    func((data) => {
    console.log(data)
    })
    // Promise
    const data = await funcAsync()
    console.log(data)
    VUJMQSPNJTJGZ΍CMVFCJSEQSPNJTJGZ"MMͳͲΛར༻͢Δ͜ͱʹΑͬͯɺ࠷খݶ
    ͷίετͰϞδϡʔϧ಺ͷϝιουΛ1SPNJTFʹରԠ͢Δ͜ͱ͕Ͱ͖Δɻ
    কདྷతͳ$BMMCBDLͷഇࢭΛߟ͑Δ৔߹ɺޓ׵ੑʹͲ͏ରॲ͢Δ͔͕೰·͍͠ɻ4VGpYΛ
    ҡ࣋͢Δ͔ɺ·ͨ͸4VGpYΛআڈͯ͠1SPNJTFͷར༻ऀʹෛՙΛ༩͑Δ͔ͷબ୒͕ඞཁɻ

    View Slide

  11. ֤ύλʔϯͷ۩ମతͳ࣮૷ํ๏͸ϒϩάͷهࣄ಺ʹɻ͓͢͢Ί͸ஈ֊తͳ1SPNJTFԽͱ
    $BMMCBDLͷഇࢭΛࢹ໺ʹೖΕ΍͍͢ʮ$BMMCBDLΛলུͨ࣌͠ʹ1SPNJTFΛฦ͢ʯํ๏ɻ
    $BMMCBDLͱ1SPNJTFͷΠϯλʔϑΣʔεΛཱ྆ͤ͞Δ
    $BMMCBDLΛলུͨ࣌͠ʹ1SPNJTFΛฦ͢
    1SPNJTFઐ༻ͷΠϯλʔϑΣʔεΛ௥Ճ͢Δ
    ‣ 1SPNJTFܕͷϞδϡʔϧΛฦ͢
    ‣ ϝιουνΣʔϯ͔Β1SPNJTFΛฦ͢
    ‣ 4VGpY෇͖ͷؔ਺͔Β1SPNJTFΛฦ͢
    ݸਓత͓͢͢Ί

    View Slide

  12. ੑೳ
    ݱ୅తͳϑϩʔ੍ޚ͸ߴ଎ͳͷ͔ʁ

    View Slide

  13. ݱ୅తͳϑϩʔ੍ޚ͸ߴ଎ͳͷ͔ʁ
    %PY#FFϕϯνʔϚʔΫͰ/PEFW؀ڥͷ$BMMCBDLͱ1SPNJTFɺBTZOD
    BXBJUͷύϑΥʔϚϯεΛܭଌ‎ݱ࣌఺Ͱ͸$BMMCBDL͕࠷଎
    εϧʔϓοτΛՄࢹԽ͍ͯ͠ΔͨΊ௿͚Ε͹௿͍
    ΄ͲύϑΥʔϚϯε͕ߴ͍͜ͱΛද͢ɻ
    $BMMCBDL͸ଞͷඇಉظॲཧʹൺ΂ͯഒҎ্ͷύ
    ϑΥʔϚϯεΛୟ͖ग़͍ͯ͠Δɻແ৚݅ʹ͢΂ͯ
    ͷ$BMMCBDLΛ๾໓ͯ͠͸͍͚ͳ͍ɻ
    ϕϯνϚʔΫͷৄࡉ͸ԼهϦϙδτϦʹ
    IUUQTHJUIVCDPN"KJEPXIJDIJTUIF
    GBTUFTUBTZODQBUUFSO

    View Slide

  14. BTZODBXBJU͸1SPNJTFΑΓ΋଎͘ͳΔ
    ࠷৽ͷ؀ڥͰ֤ඇಉظॲཧͷύϑΥʔϚϯεΛܭଌɻ7Ҏ্Ͱ͸BXBJUͷ࢓༷ม
    ߋΛ൐͏࠷దԽ͕ߦΘΕɺBTZODBXBJU͸1SPNJTFΑΓ΋ߴ଎ʹɻ
    /PEFWʹೖΔ͔΋ɻ
    Ϟδϡʔϧͷ಺෦Ͱ΋BTZODBXBJUΛར༻͠
    ͯ1SPNJTFͷΠϯλʔϑΣʔεΛ࣮૷͢Δͱ͍
    ͏ํ๏͕ΑΓ༗ޮʹͳΔɻ
    ͦͷ৔߹ɺࠓ೥݄ʹ&0-ͱͳΔ/PEFW؀
    ڥΛߟྀ͢Δ͔Ͳ͏͔͕য఺ʹɻ
    IUUQTWEFWCMPHGBTUBTZOD

    View Slide

  15. ݱ୅తͳϑϩʔ੍ޚ͸ߴ଎ͳͷ͔ʁ
    /PEFKTͷόʔδϣϯ͝ͱʹBTZODBXBJUͷύϑΥʔϚϯεΛܭଌ
    ‎BTZODBXBJU͸/PEFKTͷόʔδϣϯΞοϓʹ൐͍ܶతʹ଎౓͕޲্͍ͯ͠Δ
    ࠷దԽʹΑΓͦͷ଎౓͸$BMMCBDLʹഭΓ
    ͭͭ͋Δɻ
    1SPNJTFΑΓ΋BTZODBXBJUΛɻͦ͠
    ͯBTZODBXBJUΛ׆༻͢ΔͳΒɺϥϯλ
    ΠϜͷϝδϟʔόʔδϣϯΞοϓ͸ੵۃత
    ʹߦͳ͍͖͍ͬͯͨɻ

    View Slide

  16. ετϦʔϜରԠ
    4USFBNΛϑϩʔ੍ޚʹͲ͏૊ΈࠐΉʁ

    View Slide

  17. 4USFBNͱϑϩʔ੍ޚ͸૬ੑ͕ѱ͍
    ͜Ε·Ͱ4USFBNΛϑϩʔ੍ޚʹ૊ΈࠐΉ৔߹͸ɺ1SPNJTF
    ͰแΈࠐΈϫϯγϣοτͷΠϯλʔϑΣʔεʹม׵͢Δ͔ɺ
    ಠࣗʹΠςϨʔγϣϯରԠͤ͞Δͱ͍͏ঢ়گͩͬͨɻ
    const recv = new Promise((resolve, reject) => {
    let data = ''
    stream.on('data', (chunk) => {
    data += chunk
    })
    stream.once('end', () => {
    return resolve(data)
    })
    stream.once(‘error', (err) => {
    return reject(err)
    })
    })
    const data = await recv()
    const drain = (stream) => {
    return new Promise((resolve, reject) => {
    stream.once('data', (data) => {
    stream.pause()

    resolve(data)
    })
    stream.once('end', () => {

    resolve(null)
    })
    stream.once('error', (err) => {

    reject(err)
    })
    stream.resume()
    })
    }
    const main = async () => {
    const stream = fs.createReadStream(‘file’)
    let data = null
    while (data = await drain(stream)) {
    console.log(data)
    }
    }
    1SPNJTFͰแΈࠐΉ
    ϫϯγϣοτม׵
    ෳࡶ೉ղͳ
    ΠςϨʔγϣϯରԠ

    View Slide

  18. 4USFBNͷΤϥʔϋϯυϦϯά͸ಠಛ
    4USFBNͷجఈΫϥεͰ͋Δ&WFOU&NJUUFSͷΤϥʔϋϯυϦϯά͸ಠಛͰ͋Γɺͦ
    ͷϋϯυϦϯά࿙Ε͸ϓϩηεͷΫϥογϡʹ௚݁͢Δɻಛʹ஫ҙ͕ඞཁͳઃܭύλʔ
    ϯͷͻͱͭɻ
    ઃܭύλʔϯ ΤϥʔϋϯυϦϯά Ϋϥογϡ
    ඇಉظॲཧ
    $BMMCBDL if (err)
    &WFOU&NJUUFS emitter.on(‘error')
    1SPNJTF .catch()
    BTZODBXBJU try..catch .catch()
    ಉظॲཧ try..catch 1SPNJTFBTZODBXBJU

    View Slide

  19. "TZOD*UFSBUPSΛ࢖ͬͨ4USFBNͷϑϩʔ੍ޚ
    stream.on('data', (data) => {
    console.log(data)
    })
    stream.on('end', () => {
    console.log('finished')
    })
    stream.on(‘error', (err) => {
    console.error(err)
    })
    try {
    for await (const data of stream) {
    console.log(data)
    }
    console.log(‘finished')
    } catch (err) {
    console.error(err)
    }
    4USFBN "TZOD*UFSBUPS
    &4ͷػೳ"TZOD*UFSBUPSΛ׆༻͢Δ͜ͱͰɺ4USFBNΛγʔϜϨεʹϑϩʔ
    ੍ޚʹ૊ΈࠐΉ͜ͱ͕Ͱ͖ΔΑ͏ʹͳͬͨɻʢ&YQFSJNFOUBM"1*/PEFWʣ

    View Slide

  20. "TZOD*UFSBUPSΛ࢖ͬͨ4USFBNͷϑϩʔ੍ޚ
    ςΩετϑΝΠϧΛߦ୯ҐͰॲཧͨ͠Γɺ)551ϦΫΤετͷDIVOLΛॲཧ͢Δ৔໘ͳ
    ͲͰ׆༻Ͱ͖Δɻ౷ҰతͳΤϥʔϋϯυϦϯά͕ඇৗʹັྗతɻ
    http.createServer(async (req, res) => {
    try {
    let body = ''
    req.setEncoding('utf8')
    for await (const chunk of req) {
    body += chunk
    }
    console.log(body)
    res.end()
    } catch {
    res.statusCode = 500
    res.end()
    }
    })
    const main = async () => {
    const rl = readline.createInterface({
    input: fs.createReadStream('input.txt')
    })
    for await (const line of rl) {
    console.log(line)
    }
    }
    main().catch((err) => {
    console.error(err)
    })
    ࣍ʹ๾໓͞ΕΔͷ͸4USFBN͔ʁ

    View Slide

  21. Τϥʔॲཧ
    ඇಉظॲཧͷελοΫτϨʔε͸Ͳ͏ݟ͑Δʁ

    View Slide

  22. ඇಉظॲཧ๊͕͑ΔελοΫτϨʔεͷ՝୊ʢʣ
    ຊདྷελοΫτϨʔε͸σόοάͷॿ͚ʹͳΔ΋ͷ͕ͩɺඇಉظॲཧͷελοΫτϨʔ
    ε͸໾ʹཱͨͳ͍ɻඇಉظॲཧͷݺͼग़͠ݩ͸ɺඇಉظॲཧͷ։࢝Λ໋ྩͨ͠ίʔυͰ
    ͸ͳ͘ΠϕϯτϧʔϓͰ͋ΔͨΊɺಠཱͨ͠ελοΫτϨʔε͕දࣔ͞Εͯ͠·͏ɻ
    const exec = require('child_process').exec
    const func = (cb) => {
    exec('exit 1', (err) => {
    cb(err)
    })
    }
    func((err) => {
    console.error(err)
    })
    Error: Command failed: exit 1
    at ChildProcess.exithandler
    at ChildProcess.emit
    at maybeClose
    at Socket.stream.socket.on
    at Socket.emit
    at Pipe._handle.close
    ࣮ߦ݁Ռ
    ݺͼग़͠ݩͷGVODؚ͕·Ε͓ͯΒͣ
    ΤϥʔͷൃੜՕॴ͕ಛఆͰ͖ͳ͍

    View Slide

  23. ඇಉظॲཧ๊͕͑ΔελοΫτϨʔεͷ՝୊ʢʣ
    "TZOD'VODUJPOͷ؀ڥԼͰ͸BXBJUʹΑͬͯελοΫτϨʔε͕෼཭͞ΕΔɻBXBJU
    Ͱதஅ͞Εͨؔ਺͸ϚΠΫϩλεΫΩϡʔ͔Βॲཧ͕࠶։͞ΕΔͨΊɺલड़ͷ໰୊ͱಉ
    ༷ʹؔ਺ͷݺͼग़͠ܦ࿏͕ҡ࣋͞Εͣɺ੾Γ཭͞Εͯ͠·͏ɻ
    const func = async () => {
    await 'interrupt'
    throw new Error('error')
    }
    const main = async () => {
    await func()
    }
    main().catch((err) => {
    console.error(err)
    })
    Error: error
    at func
    at process._tickCallback
    at Function.Module.runMain
    at startup
    at bootstrapNodeJSCore
    ࣮ߦ݁Ռ
    ݺͼग़͠ݩͷNBJOؚ͕·Ε͓ͯΒͣ
    ΤϥʔͷൃੜՕॴ͕ಛఆͰ͖ͳ͍

    View Slide

  24. ඇಉظॲཧ๊͕͑ΔελοΫτϨʔεͷ՝୊ʹରॲ͢Δ
    લड़ͨ͠ελοΫτϨʔεͷ՝୊͸ҎԼʹྻڍͨ͠ํ๏ʹΑͬͯରॲͰ͖Δɻ
    ํ๏ ֓ཁ
    JOTQFDUCSL$ISPNF%FW5PPMT
    w ༷ʑͳσόοάػೳΛ಺แ
    w Φʔόʔϔου͋Γʢߴίετʣ
    BTZOD@IPPLTUSBDFDMBSJGZ
    w ߴ͍Մಡੑͱ࢖͍উखͷྑ͞
    w Φʔόʔϔου͋Γ
    BTZODTUBDLUSBDFT
    w Φʔόʔϔουͳ͠
    w BTZODBXBJUͷ՝୊ͷΈղܾՄೳ

    View Slide

  25. ඇಉظॲཧ๊͕͑ΔελοΫτϨʔεͷ՝୊ʹରॲ͢Δ
    JOTQFDUCSL$ISPNF%FW5PPMT
    JOTQFDUCSLΦϓγϣ
    ϯ෇͖Ͱىಈɻ
    $ISPNF%FW5PPMT͔
    Β֬ೝ͢Δɻ
    ελοΫτϨʔεʹ͸දࣔ͞Εͳ͍
    ܦ࿏΋දࣔ͞ΕΔ

    View Slide

  26. ඇಉظॲཧ๊͕͑ΔελοΫτϨʔεͷ՝୊ʹରॲ͢Δ
    BTZOD@IPPLTUSBDFDMBSJGZ
    USBDFϞδϡʔϧ͸ඪ४ϞδϡʔϧͷBTZOD@IPPLTΛར༻ͯ͠ɺඇಉظॲཧʹಠࣗίʔ
    υΛࠩ͠ࠐΈελοΫτϨʔεΛ֦ு͢ΔɻDMBSJGZϞδϡʔϧ͸ෆཁͳελοΫτϨʔ
    εΛআڈͯ͠ελοΫτϨʔεͷՄಡੑΛ޲্ͤ͞Δɻʢݸਓత͓͢͢Ίʣ
    ❯ node -r trace -r clarify exec.js
    Error: Command failed: exit 1
    at func
    at Object.
    ࣮ߦ݁Ռ
    ݺͼग़͠ݩͷGVODؚ͕·Ε
    ෆཁͳߦ΋࡟আ͞Ε͍ͯΔ
    const exec = require('child_process').exec
    const func = (cb) => {
    exec('exit 1', (err) => {
    cb(err)
    })
    }
    func((err) => {
    console.error(err)
    })

    View Slide

  27. ඇಉظॲཧ๊͕͑ΔελοΫτϨʔεͷ՝୊ʹରॲ͢Δ
    BTZODTUBDLUSBDFT
    7Ҏ্ͷ؀ڥͰ͜ͷΦϓγϣϯΛ༗ޮʹ͢ΔͱɺBXBJUʹؚ·ΕΔ৘ใΛ࢖ͬͯ
    ඇಉظॲཧΛލ͍ͩελοΫτϨʔε͕ߏங͞ΕΔΑ͏ʹͳΔɻ෦෼తͳ໰୊ղܾखஈ
    Ͱ͸͋Δ͕ɺ࣮ߦதͷγεςϜʹෛՙΛ༩͑ͳֵ͍৽తͳػೳɻ
    const func = async () => {
    await 'interrupt'
    throw new Error('error')
    }
    const main = async () => {
    await func()
    }
    main().catch((err) => {
    console.error(err)
    })
    ❯ node --async-stack-traces await.js
    Error: error
    at func
    at process._tickCallback
    at Function.Module.runMain
    at startup
    at bootstrapNodeJSCore
    at async main
    ࣮ߦ݁Ռ
    ݺͼग़͠ݩͷNBJOؚ͕·Ε͍ͯΔ
    ·ͩϑϥά෇͖ͷػೳɻࠓޙͷϑϥάղআʹظ଴

    View Slide

  28. ϑϩʔ੍ޚͷ࠷దԽ͸$BMMCBDLΛ1SPNJTFʹม͚͑ͨͩͰ͸
    ऴΘΒͳ͍ɻকདྷΛݟਾ͑ɺ͞ΒͳΔ࠷దԽΛਐΊΑ͏ʂ
    ‣ BTZODBXBJU͸վྑ͞Εɺߴ଎Խ͠ଓ͚͍ͯΔ
    ‣ 1SPNJTFΑΓ΋BTZODBXBJUͷੵۃతͳ׆༻Λ
    ‣ 4USFBNͷϑϩʔ੍ޚʹର͢Δ࠷దԽ͸ؒۙ
    ‣ "TZOD*UFSBUPSͷ&YQFSJNFOUBMղআʹظ଴
    ‣ ඇಉظελοΫτϨʔεͷ՝୊͸վળͷஹ͠ΞϦ

    View Slide