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

君だけのオリジナル async / await を作ろう / TSKaigi 2025

君だけのオリジナル async / await を作ろう / TSKaigi 2025

Avatar for Susisu

Susisu

May 24, 2025
Tweet

More Decks by Susisu

Other Decks in Technology

Transcript

  1. ࠓ೔࿩͢͜ͱ ᶃ async / await Λ δΣωϨʔλͰ࠶࣮૷ ᶄ TypeScript ʹΑΔܕ෇͚

    ᶅ Ԡ༻ͦͷ 1: Result ܕ ᶆ Ԡ༻ͦͷ 2: ΤϑΣΫτ JS TS ᶃ ᶄ ᶅ ᶆ async / await δΣωϨʔλ Result ܕ ΤϑΣΫτ
  2. ࠓ೔࿩͢͜ͱ ᶃ async / await Λ δΣωϨʔλͰ࠶࣮૷ ᶄ TypeScript ʹΑΔܕ෇͚

    ᶅ Ԡ༻ͦͷ 1: Result ܕ ᶆ Ԡ༻ͦͷ 2: ΤϑΣΫτ JS ᶃ async / await δΣωϨʔλ
  3. ࠓ೔࿩͢͜ͱ ᶃ async / await Λ δΣωϨʔλͰ࠶࣮૷ ᶄ TypeScript ʹΑΔܕ෇͚

    ᶅ Ԡ༻ͦͷ 1: Result ܕ ᶆ Ԡ༻ͦͷ 2: ΤϑΣΫτ JS TS ᶃ ᶄ async / await δΣωϨʔλ
  4. ࠓ೔࿩͢͜ͱ ᶃ async / await Λ δΣωϨʔλͰ࠶࣮૷ ᶄ TypeScript ʹΑΔܕ෇͚

    ᶅ Ԡ༻ͦͷ 1: Result ܕ ᶆ Ԡ༻ͦͷ 2: ΤϑΣΫτ JS TS ᶃ ᶄ ᶅ async / await δΣωϨʔλ Result ܕ
  5. ࠓ೔࿩͢͜ͱ ᶃ async / await Λ δΣωϨʔλͰ࠶࣮૷ ᶄ TypeScript ʹΑΔܕ෇͚

    ᶅ Ԡ༻ͦͷ 1: Result ܕ ᶆ Ԡ༻ͦͷ 2: ΤϑΣΫτ JS TS ᶃ ᶄ ᶅ ᶆ async / await δΣωϨʔλ Result ܕ ΤϑΣΫτ
  6. async / await • Promise Λ࢖ͬͨίʔυΛݟ௨͠ྑ͘ॻͨ͘Ίʹ async / await ͸ෆՄܽ

    // before doX().then((x) => doY(x).then((y) => doZ(y).then((z) => // ... ) ) ); // after const x = await doX(); const y = await doY(x); const z = await doZ(y); // ...
  7. async / await ͷྺ࢙ • Promise ͷඪ४Խ͔Β async / await

    ͷඪ४Խ·Ͱ͸λΠϜϥά͕͋ͬͨ • Promise: ES2015 • async / await: ES2017 • ਓʑ͸τϥϯεύΠϥΛ࢖ͬͯ async / await ΛઌऔΓ͍ͯͨ͠ • Babel, TypeScript • ͋Δ͍͸δΣωϨʔλΛ࢖ͬͯ async / await ͬΆ͍ίʔυΛॻ͍͍ͯͨ • tj/co, Koa
  8. δΣωϨʔλ • ES2015 Ͱඪ४Խ͞Εͨݴޠػೳ • δΣωϨʔλؔ਺ function* ͱ yield ࣜͰΠςϨʔλΛखଓ͖తʹهड़Ͱ͖Δ

    • ͜Ε͸தஅɾ࠶։Ͱ͖Δܭࢉ (ίϧʔνϯ) ͱͯ͠ݟΔ͜ͱ΋Ͱ͖Δ function* range(from, to, step = 1) { for (let n = from; n < to; n += step) { yield n; } }
  9. δΣωϨʔλؔ਺ͷதஅɾ࠶։ function* myFunc() { const x = yield "X"; const

    y = yield "Y"; return x + y; } const gen = myFunc(); ← ·࣮ͩߦ͞Ε͍ͯͳ͍
  10. δΣωϨʔλؔ਺ͷதஅɾ࠶։ function* myFunc() { const x = yield "X"; const

    y = yield "Y"; return x + y; } const gen = myFunc(); gen.next(); // { done: false, value: "X" } ← yield Ͱதஅ
  11. δΣωϨʔλؔ਺ͷதஅɾ࠶։ function* myFunc() { const x = yield "X"; //

    x = 1 const y = yield "Y"; return x + y; } const gen = myFunc(); gen.next(); // { done: false, value: "X" } gen.next(1); // { done: false, value: "Y" } ← ࠶։͞Εͯ;ͨͨͼ yield Ͱதஅ
  12. δΣωϨʔλؔ਺ͷதஅɾ࠶։ function* myFunc() { const x = yield "X"; //

    x = 1 const y = yield "Y"; // y = 2 return x + y; } const gen = myFunc(); gen.next(); // { done: false, value: "X" } gen.next(1); // { done: false, value: "Y" } gen.next(2); // { done: true, value: 3 } ← return Ͱऴྃ
  13. async / await ͷ࠶࣮૷ • δΣωϨʔλؔ਺ͷதஅɾ࠶։Λར༻͢Ε͹ɺasync / await Λ࠶ݱͰ͖ͦ͏ •

    await ͸ Promise ͷ׬ྃ·Ͱؔ਺ͷ࣮ߦΛதஅ͢Δ͜ͱʹ΄͔ͳΒͳ͍ • جຊతͳΞΠσΞ: • Promise Λ yield ͯ͠ܭࢉΛதஅ • Promise ͕׬ྃͨ͠ΒܭࢉΛ࠶։
  14. async / await ͷ࠶࣮૷ async function myFunc() { const x

    = await doX(); const y = await doY(x); return y; } const promise = myFunc(); function* myFunc() { const x = yield doX(); const y = yield doY(x); return y; } const promise = run(myFunc());
  15. async / await ͷ࠶࣮૷ async function myFunc() { const x

    = await doX(); const y = await doY(x); return y; } const promise = myFunc(); function* myFunc() { const x = yield doX(); const y = yield doY(x); return y; } const promise = run(myFunc()); → ͍͍ײ͡ʹδΣωϨʔλΛ࣮ߦͯ͘͠ΕΔؔ਺ run() Λ࣮૷͢Δ
  16. run() ͷ࣮૷ (1/3) function run(comp) { function onFulfilled(value) { //

    Promise 成功時の処理 let res; try { res = comp.next(value); // 計算を再開 } catch (e) { return Promise.reject(e); // throw } if (res.done) return Promise.resolve(res.value); // return return res.value.then(onFulfilled, onRejected); // yield }
  17. run() ͷ࣮૷ (1/3) function run(comp) { function onFulfilled(value) { //

    Promise 成功時の処理 let res; try { res = comp.next(value); // 計算を再開 } catch (e) { return Promise.reject(e); // throw } if (res.done) return Promise.resolve(res.value); // return return res.value.then(onFulfilled, onRejected); // yield }
  18. run() ͷ࣮૷ (1/3) function run(comp) { function onFulfilled(value) { //

    Promise 成功時の処理 let res; try { res = comp.next(value); // 計算を再開 } catch (e) { return Promise.reject(e); // throw } if (res.done) return Promise.resolve(res.value); // return return res.value.then(onFulfilled, onRejected); // yield } δΣωϨʔλؔ਺͔ΒͷԠ౴͸ throw, return, yield ͷ 3 ௨Γ͕͋ΓಘΔ
  19. run() ͷ࣮૷ (2/3) function onRejected(error) { // Promise 失敗時の処理 let

    res; try { res = comp.throw(error); // 計算を再開 } catch (e) { return Promise.reject(e); // throw } if (res.done) return Promise.resolve(res.value); // return return res.value.then(onFulfilled, onRejected); // yield }
  20. run() ͷ࣮૷ (2/3) function onRejected(error) { // Promise 失敗時の処理 let

    res; try { res = comp.throw(error); // 計算を再開 } catch (e) { return Promise.reject(e); // throw } if (res.done) return Promise.resolve(res.value); // return return res.value.then(onFulfilled, onRejected); // yield } onFul fi lled ͱͷࠩ෼͸͚ͩ͜͜ yield ͷՕॴͰ throw ͢Δ
  21. run() ͷ࣮૷ (3/3) // 計算を先頭から開始 return onFulfilled(); } function* myFunc()

    { const x = yield doX(); const y = yield doY(x); return y; } const promise = run(myFunc());
  22. TypeScript ʹΑΔܕ෇͚ function* myFunc() { const x = yield doX();

    const y = yield doY(x); return y; } const promise = run(myFunc()); • yield ࣜʹܕ͕෇͚ΒΕΔঢ়ଶΛ໨ࢦ͢ • (run() ͷ࣮૷಺෦ʹର͢Δܕ෇͚͸໨ࢦ͞ͳ͍)
  23. 1. ܾ·ͬͨํ๏Ͱ࣮ߦ͞ΕΔอূ͕ͳ͍ function* myFunc() { const x = yield doX();

    const y = yield doY(x); return y; } const promise = run(myFunc()); // run() を使っているなら OK const what = [...myFunc()]; // そうでないならダメ
  24. 1. ܾ·ͬͨํ๏Ͱ࣮ߦ͞ΕΔอূ͕ͳ͍ function* myFunc() { const x = yield doX();

    const y = yield doY(x); return y; } const promise = run(myFunc()); // run() を使っているなら OK const what = [...myFunc()]; // そうでないならダメ → ن໿ͱ͍͏͜ͱʹͯ͠͠·͏... • ໓ଟʹ΍Βͳ͍Ͱ͠ΐ͏ / ඞཁʹͳͬͨΒ Linter Λ࡞Ζ͏
  25. 2. yield ࣜ͝ͱʹҟͳΔܕ͕෇͚ΒΕͳ͍ • δΣωϨʔλͷܕ Generator<T, TReturn, TNext> • T

    ... yield a ͷ a ͷܕ • TReturn ... return a ͷ a ͷܕ • TNext ... gen.next(b) ͷ b ͷܕ = yield a ͷܕ function* myFunc(): Generator<T, TReturn, TNext> { // ... }
  26. 2. yield ࣜ͝ͱʹҟͳΔܕ͕෇͚ΒΕͳ͍ • yield ࣜͷܕ = TNext ͕ 1

    छྨ͔͠ͳ͍ • ҎԼͷίʔυͰݴ͏ͱ x ͱ y ͷܕ͕ಉ͡ʹͳͬͯ͠·͏ function* myFunc(): Generator<T, TReturn, TNext> { const x = yield doX(); // x: TNext const y = yield doY(x); // y: TNext return y; }
  27. • yield ࣜͷܕ = TNext ͕ 1 छྨ͔͠ͳ͍ • ҎԼͷίʔυͰݴ͏ͱ

    x ͱ y ͷܕ͕ಉ͡ʹͳͬͯ͠·͏ → yield* ࣜΛ࢖͏ function* myFunc(): Generator<T, TReturn, TNext> { const x = yield doX(); // x: TNext const y = yield doY(x); // y: TNext return y; } 2. yield ࣜ͝ͱʹҟͳΔܕ͕෇͚ΒΕͳ͍
  28. yield* ࣜΛ࢖͏ • yield* iter ͸ Iterable ΛδΣωϨʔλؔ਺಺Ͱల։͢Δߏจ • yield*

    iter ͷܕ͸ iter ʹԠܾͯ͡·Δ • iter ͷܕ͕ Iterable<S, SReturn, SNext> ͷͱ͖ɺyield* iter ͷܕ͸ SReturn • → ࣜ͝ͱʹҟͳΔܕΛ࣋ͯΔ
  29. yield* ࣜΛ࢖͏ • yield* ࣜʹ༩͑Δ஋͸ Iterable Ͱ͋Δඞཁ͕͋Δ • ୯७ʹ yield

    a Λ yield* a ͷΑ͏ʹஔ͖׵͑Δ͜ͱ͸Ͱ͖ͳ͍ • yield* toIterable(a) ͷΑ͏ʹ Iterable ʹม׵͠ͳ͍ͱ͍͚ͳ͍ • ม׵͸ͳΜͰ΋ྑ͍Θ͚Ͱ͸ͳ͍ • ݩʑͷ yield a ͱஔ͖׵͑ޙͷ yield* toIterable(a) ͸ಉ͡ಈ͖Λͯ͠΄͍͠
  30. Iterable ΁ͷม׵ type Comp<T> = Generator<Promise<any>, T, any>; function* waitFor<T>(promise:

    Promise<T>): Comp<Awaited<T>> { return yield promise; } • ୯Ұͷ஋Λ yield ͠ɺͦͷ஋Λ return ͢Δ͚ͩͷδΣωϨʔλΛ࡞Δ • yield* waitFor(a) ͸ yield a ͱಉ͡ಈ࡞Λ͢Δ͕ɺܕ৘ใ͕૿͍͑ͯΔ
  31. Iterable ʹม׵͢Δ declare function run<T>(comp: Comp<T>): Promise<Awaited<T>>; function* myFunc(): Comp<Y>

    { const x = yield* waitFor(doX()); // x: X const y = yield* waitFor(doY(x)); // y: Y return y; } const promise = run(myFunc()); // promise: Promise<Y> • run() ʹରͯ͠͸֎͔ΒܕΛ͚ͭΔ͚ͩͰ׬੒
  32. ͜͜·Ͱͷ·ͱΊ • ࣗ࡞ async / await ʹܕΛ෇͚Δʹ͸ 2 ͭͷน͕͋Δ •

    δΣωϨʔλ͕Ͳ͏ݺ͹ΕΔ͔อূ͕ͳ͍ • yield ࣜ͝ͱʹҟͳΔܕ͕෇͚ΒΕͳ͍ • લऀ͸ن໿ԽͰΧόʔՄೳɺޙऀ͸ yield ࣜͷ୅ΘΓʹ yield* ࣜΛ࢖͏͜ͱͰ ৐Γӽ͑ΒΕΔ
  33. Promise Ҏ֎΁ͷԠ༻ • ྫ͑͹ Result ܕ΋͜Εʹ౰ͯ͸·Δ • then() ʹରԠ͢Δͷ͸ԼͷΑ͏ͳ৚݅෼ذ type

    Result<T, E> = Ok<T> | Err<E>; if (!res.isOk) return res; return cont(res.value);
  34. Result ܕ΁ͷԠ༻ • Promise Ͱ͸ then() ͕ await (yield*) ʹஔ͖׵͑ΒΕͨ

    // before doX().then((x) => doY(x).then((y) => doZ(y).then((z) => // ... ) ) ); // after const x = await doX(); const y = await doY(x); const z = await doZ(y); // ...
  35. Result ܕ΁ͷԠ༻ • Result Ͱ΋ಉ͡Α͏ʹஔ͖׵͑ΒΕΔ → ΤϥʔϋϯυϦϯάΛ؆ུԽ // before const

    x = doX(); if (!x.isOk) return x; const y = doY(x.value); if (!y.isOk) return y; const z = doZ(y.value); if (!z.isOk) return z; // ... // after const x = yield* doX(); const y = yield* doY(x); const z = yield* doZ(y); // ...
  36. Result ܕ΁ͷԠ༻ • ࣮૷͸ׂѪ (ઌ΄Ͳͱಉ͡ susisu/tskaigi2025 ʹ׬શ൛͕͋Γ·͢) • run() ͷఆٛ͸

    Promise ͷ࣌ͱ΄΅ಉ͡ (then() ͕ҟͳΔ͚ͩ) type Comp<T, E> = Generator<Result<any, E>, T, any>; declare function run<T, E>(comp: Comp<T, E>): Result<T, E>; function* myFunc(): Comp<Y, Error> { /* ... */ } const result = run(myFunc()); // result: Result<Y, Error>
  37. Result ܕࣗମΛ Iterable ʹ͢Δ • Promise ͱ͸ҟͳΓɺResult ܕࣗମΛ Iterable ʹ΋Ͱ͖Δ

    • yield* ࣜΛ࢖͏ͱ͖ͷม׵ΛखͰॻ͘ඞཁ͕ͳ͘ͳͬͯศར class Ok<T> { // ... *[Symbol.iterator](): Comp<T, never> { return yield this; } }
  38. ͜͜·Ͱͷ·ͱΊ • δΣωϨʔλΛ࢖ͬͨςΫχοΫ͸ Promise Ҏ֎ʹ΋Ԡ༻͕Մೳ • yield ͞Εͨ஋ͱ࢒ΓͷܭࢉΛ্ख͚ͬͭ͘͘ΒΕͨΒྑ͍ (Ϟφυ) •

    ྫͱͯ͠ Result ܕͷ৔߹Λ঺հ • ଞʹ΋͍Ζ͍Ζ • neverthrow, E ff ect ͳͲͷϥΠϒϥϦ • ύʔαίϯϏωʔλ • etc.
  39. • ͜Μͳ;͏ʹϓϩάϥϜ͕ॻ͚Δͱͨ͠Βʁ ΤϑΣΫτγεςϜ // 型に現れないエフェクトを発生させたらコンパイルエラー function* main(): Eff<void, "fs"> {

    const icon = yield* httpGet("https://susisu.ch/icon.svg"); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ yield* writeFile("./icon.svg", icon); }
  40. ΤϑΣΫτγεςϜ • ͜Μͳ;͏ʹϓϩάϥϜ͕ॻ͚Δͱͨ͠Βʁ // 通常は実際に読み書き runAsync( interpret(main(), { net: interpretNet,

    fs: interpretFs, }), ); // テストではモック実装 runPure( interpret(main(), { net: mockNet, fs: mockFs, }), );
  41. ΤϑΣΫτγεςϜ • ͜Μͳ;͏ʹϓϩάϥϜ͕ॻ͚Δͱͨ͠Βʁ • → ॻ͚·ͨ͠ (࣮ݧతͳϥΠϒϥϦΛ࡞Γ·ͨ͠) • https://github.com/susisu/e ff

    ectful • ͜͜·Ͱʹ঺հͨ͠ͷ͸શ࣮ͯࡍʹಈ࡞͢Δίʔυ • ཧ࿦తʹ͸ Algebraic E ff ects & Handlers ͱݺ͹ΕΔύϥμΠϜ
  42. ΤϑΣΫτγεςϜͷ࣮૷ • δΣωϨʔλͷܕఆٛ͸ Promise ΍ Result ͷ৔߹ͱجຊ͸ಉ͡ • ͨͩ͠۩ମతͳσʔλ (Ϟφυ)

    ͷ෦෼͸ΤϑΣΫτͱͯ͠ந৅Խ͓͍ͯͯ͠ɺ
 ޙ͔Β࣮ߦ͢ΔՕॴ (ϋϯυϥ) Ͱܾఆ͢Δ // before type Comp<T> = Generator<Promise<any>, T, any>; type Comp<T, E> = Generator<Result<any, E>, T, any>; // after type Eff<T, Row> = Generator<Effect<Row, any>, T, any>;
  43. ࣌ؒͶ͐ʔ • ໘ന͍͜ͱ͕ͨ͘͞Μ͋Δ͚Ͳ࿩͕࣌ؒ͢଍Γͳ͍ʂ • ࣮૷ʹ࢖ͬͨςΫχοΫ • ϢχΦϯܕͰΤϑΣΫτͷ row Λૉ௚ʹදݱ (ׂͱ

    TypeScript ಛ༗) • interface ͷએݴϚʔδΛ࢖ͬͨߴ֊ܕ (ΤϑΣΫτͷந৅Խʹඞཁ) • GADT (ந৅Խͨ͠ΤϑΣΫτͷ໭Γ஋ͷܕΛݻఆ) • ΤϑΣΫτγεςϜΛ࢖ͬͯ۩ମతʹԿΛ͢Δͷ͔ʁ (࢖͑Δͷ͔ʁ) • Ask the Speaker ΍࠙਌ձͰ࿩͠·͠ΐ͏ :pray:
  44. ·ͱΊ (1/2) • δΣωϨʔλؔ਺͸தஅɾ࠶։Ͱ͖Δܭࢉͱͯ͠ݟΔ͜ͱ͕Ͱ͖Δ • ͜ΕΛ࢖͑͹ async / await ૬౰ͷه๏Λࣗ࡞Ͱ͖Δ

    • TypeScript Ͱ͸ͪΐͬͱ͓ͨ͠·͡ͳ͍Ͱ֓Ͷ্ख͘ܕΛ෇͚ΒΕΔ • yield Λ yield* ʹஔ͖׵͑Δ / ͦͷͨΊʹ Iterable ʹม׵͢Δ
  45. ·ͱΊ (2/2) • δΣωϨʔλΛ࢖ͬͨςΫχοΫ͸ Promise Ҏ֎ʹ΋Ԡ༻͕Մೳ • yield ͞Εͨ஋ͱ࢒ΓͷܭࢉΛ্ख͚ͬͭ͘͘ΒΕͨΒྑ͍ (Ϟφυ)

    • ۩ମతʹ͸ Result ܕͳͲ • ͞Βʹ͸ Algebraic E ff ects ͷΑ͏ͳ΋ͷ΋࣮ݱͰ͖Δ • TypeScript ໘ന͍