Skip to content

Instantly share code, notes, and snippets.

@swannodette
Last active January 9, 2021 16:09
Show Gist options
  • Star 29 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save swannodette/6385166 to your computer and use it in GitHub Desktop.
Save swannodette/6385166 to your computer and use it in GitHub Desktop.
(ns blog.errors.core
(:require-macros
[cljs.core.async.macros :refer [go]]
[blog.utils.macros :refer [<?]])
(:require
[cljs.core.async :refer [>! <! chan close!]]))
;; convert Node.js async function into a something
;; that returns a value or error on a channel
(defn run-task [f & args]
(let [out (chan)
cb (fn [err & results]
(go (if err
(>! out err)
(>! out results))
(close! out)))]
(apply f (concat args [cb]))
out))
(def fs (js/require "fs"))
;; BOOM!!! we can convert async errors into exceptions
(go (try
(let [x (<? (run-task (.-readFile fs) "foo.txt" "utf8"))]
(.log js/console "Success" x))
(catch js/Error e
(.log js/console "Oops" e))))
(ns blog.errors.core
(:require-macros
[cljs.core.async.macros :refer [go]])
(:require
[cljs.core.async :refer [>! <! chan close!]]))
(defn error? [x]
(instance? js/Error x))
(defn run-task [f & args]
(let [out (chan)
cb (fn [err & results]
(go (if err
(>! out err)
(>! out results))
(close! out)))]
(apply f (concat args [cb]))
out))
;; wrap a task to run in a thunk so a supervisor can call it
(defn task [& args]
(fn [] (apply run-task args)))
;; a policy so a supervisor knows to retry a task
(defprotocol IPolicy
(-retry? [this err attempts]))
;; retry a task up to N times policy
(defn maxtries [n]
(reify
IPolicy
(-retry? [_ err attempts]
(< attempts n))))
;; take a task to run and a policy for retries
(defn supervisor [task policy]
(go (loop [attempts 0 err nil]
(if-not (-retry? policy err attempts)
err
(let [v (<! (task))]
(if (error? v)
(let [attempts (inc attempts)]
(do (.log js/console "Attempt" attempts)
(recur attempts v)))
v))))))
(def fs (js/require "fs"))
;; go macro lets us write straight line code even though supervisor
;; implements sophisticated async behavior
(go (let [result (<! (supervisor
(task (.-readFile fs) "foo.txt" "utf8")
(maxtries 3)))]
(if-not (error? result)
(.log js/console (.split (first result) "\n"))
(.log js/console result))))
@gdeer81
Copy link

gdeer81 commented Aug 30, 2013

dat core.async 0_o

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment