$30 off During Our Annual Pro Sale. View Details »

kitchen-async: a promising (?) Promise library,...

OHTA Shogo
January 29, 2018

kitchen-async: a promising (?) Promise library, or a poor man's core.async

2018/01/29のLisp meetup #60の発表資料です。

OHTA Shogo

January 29, 2018
Tweet

More Decks by OHTA Shogo

Other Decks in Programming

Transcript

  1. ࣗݾ঺հ ‣ 5XJUUFS!BUIPT ‣ (JU)VCBUIPT ‣ $MPKVSF 4DSJQU ॻ͍ͯ·͢ 

    3FBDU/BUJWFΞϓϦΛ$MPKVSF4DSJQUͰ  "MFYB4LJMMΛ$MPKVSF4DSJQUͰ
  2. 1SPNJTF ‣ 1SPNJTF͸ඇಉظͳܭࢉ݁ՌΛอ࣋͢Δσʔλܕ ‣ ίʔϧόοΫΛҾ਺ʹड͚Δ୅ΘΓʹ1SPNJTFΛฦ͢Α͏ ʹ͓ͯ͘͠ͱʜ const readFileAsync = (path)

    => { return new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if (err) return reject(err) resolve(data) }) }) }
  3. +BWB4DSJQUք۾ͷඇಉظࣄ৘ ‣ &$."4DSJQU &4 ͷ࢓༷΁ͷऔΓࠐΈ  &41SPNJTF  &4BTZODBXBJU ‣

    8FCඪ४ͷ"1*Ͱͷར༻  'FUDI"1*  8FC35$ͷϝσΟΞऔಘ  4FSWJDF8PSLFS FUDFUD ‣ αʔυύʔςΟͷOQNϞδϡʔϧͰͷར༻ +BWB4DSJQUͷ༷ʑͳ"1*ʹ1SPNJTF͕࢖ΘΕͭͭ͋Δ
  4. DPSFBTZODͰͷඇಉظॲཧ (require ‘[clojure.core.async :as a]) (defn read-file [path] (let [ch

    (a/chan)] (fs.readFile path (fn [err data] (a/put! ch (or err data)))) ch)) (a/go (let [data (a/<! (read-file “input.txt”))] (js/console.log data)))
  5. ར༻ऀ͔Βͷ͓೰Έ ʮ$MPKVSF4DSJQU͔Βͩͱ1SPNJTFΛѻ͏ίʔυ Λ͖ͬ͢Γॻ͚·ͤΜʯ ୅உੑձࣾһ (-> (readFileAsync “input.txt”) (.then (fn [text]

    (js/JSON.parse text))) (.then (fn [json] (js/console.log json))) (.catch (fn [err] (js/console.log err)))) ˞͜Ε͸ݸਓͷײ૝Ͱײ͡ํʹ͸ݸਓ͕ࠩ͋Γ·͢
  6. ར༻ऀ͔Βͷ͓೰Έ ʮ$MPKVSF4DSJQU͔Βͩͱ1SPNJTFΛѻ͏ίʔυ Λ͖ͬ͢Γॻ͚·ͤΜʯ ୅உੑձࣾһ (-> (puppeteer/launch) (.then (fn [browser] (->

    (.newPage browser) (.then (fn [page] (.then (.goto page “https://www.google.com") #(.screenshot page #js{:path “image.png"})))) (.then #(.close browser))))))) ˞͜Ε͸ݸਓͷײ૝Ͱײ͡ํʹ͸ݸਓ͕ࠩ͋Γ·͢
  7. ར༻ऀ͔Βͷ͓೰Έ ‣ 1SPNJTFϔϏʔͳ"1*Λ࢖͏ͱ͖ʹ౎౓νϟωϧʹม׵ͨ͘͠ͳ͍ ‣ HPϒϩοΫ͸े෼ʹෳࡶͳϚΫϩ  ల։ܗ΋σΧ͍  XFC"1*Λճୟͨ͘Ί͚ͩʹHPϒϩοΫΛॻ͖ͨ͘ͳ͍ ‣

    DPSFBTZOD͸Τϥʔॲཧ͕͏·͘ͳ͍ ‣ TFMGIPTUFE$-+4 -VNP1MBODL Ͱ͸ͦͷ··Ͱ͸ಈ͔ͳ͍ ʮDPSFBTZOD͸͔ͨ͠ʹڧྗͳಓ۩ͳΜ͚ͩͲɺ ͬ͘͠Γ͜ͳ͍৔߹΋͋Γ·͢ΑͶʯ ୅உੑձࣾһ ˞͜Ε͸ݸਓͷײ૝Ͱײ͡ํʹ͸ݸਓ͕ࠩ͋Γ·͢
  8. QSPNJTFɾUIFOɾDBUDI ‣ +4ͷ1SPNJTFͱ ΄΅ ౳Ձͳ"1*Λఏڙ͍ͯ͠Δ (require ‘[kitchen-async.promise :as p]) (->

    (p/promise [resolve reject] (resolve 42)) (p/then (fn [x] (js/console.log x))) (p/catch* (fn [err] (js/console.log err))))
  9. QMFUϚΫϩ (p/let [<var1> <init1> <var2> <init2> …] <body>) (p/then <init1>

    (p/then (fn [<var1>] (p/then <init2> (fn [<var2>] … <body> …))))) ্ͷϑΥʔϜ͸͓͓ΉͶҎԼͷΑ͏ʹల։͞ΕΔ
  10. QMFUϚΫϩ (p/let [browser (puppeteer/launch) page (.newPage browser)] (.goto page "https://www.example.com")

    (.screenshot page #js{:path "image.png"}) (.close browser)) (-> (puppeteer/launch) (.then (fn [browser] (-> (.newPage browser) (.then (fn [page] (.then (.goto page “https://www.example.com”) #(.screenshot page #js{:path "image.png"})))) (.then #(.close browser))))))) ্ͷίʔυ͸QMFUΛ࢖ͬͯҎԼͷΑ͏ʹॻ͖׵͑ΒΕΔ
  11. εϨοσΟϯάϚΫϩ (p/let [x (f) y (g x)] (h y)) (p/->

    (f) g h) ্ͷίʔυ͸QΛ࢖ͬͯҎԼͷΑ͏ʹॻ͖׵͑ΒΕΔ w Q QTPNF QTPNF΋ಉ༷ʹ࢖͑Δ
  12. QSPNJTF ‣ ೚ҙͷ஋Λ1SPNJTFʹม׵ DPFSDJPO ͢Δؔ਺ (defprotocol Promisable (->promise [this])) (extend-protocol

    Promisable js/Promise (->promise [this] this) default (->promise [this] (p/promise [resolve] (resolve this)))
  13. ҉໧ͷQSPNJTF ‣ LJUDIFOBTZODͷ͢΂ͯͷ"1*͸҉໧ʹQSPNJTFΛద༻ ‣ 1SPNJTFҎ֎ͷ஋΋1SPNJTFͱ۠ผͳ͘࢖͏͜ͱ͕Ͱ͖Δ (p/let [x (f), y (g

    x)] (h y)) (p/then p f) (p/then (->promise p) f) (p/let [x (->promise (f)), y (->promise (g x))] (h y))
  14. DPSFBTZODνϟωϧͱͷ࿈ܞ (ns kitchen-async.promise.from-channel (:require [clojure.core.async :as a] [clojure.core.async.impl.channels :refer [ManyToManyChannel]]

    [kitchen-async.promise :as p])) (extend-protocol p/Promisable ManyToManyChannel (->promise [ch] (p/promise [resolve reject] (a/go (let [x (a/<! ch)] (if (instance? js/Error x) (reject x) (resolve x)))))))
  15. ࣮༻ྫ ‣ DPSFBTZODͱಉ౳ͷ͜ͱ͕HPϚΫϩΛ࢖ΘͣʹͰ͖Δ ‣ ͨͩ͠ɺύϑΥʔϚϯεతʹ͸DPSFBTZODͷํ͕͍͍ (defn debounce ([c ms] (debounce

    (a/chan) c ms)) ([c' c ms] (p/loop [start nil loc c] (if (nil? start) (do (a/put! c' loc) (p/recur (js/Date.) nil)) (p/let [loc c] (if (>= (- (js/Date.) start) ms) (p/recur nil loc) (p/recur (js/Date.) loc))))) c'))