Skip to content

Instantly share code, notes, and snippets.

@megakorre
Created April 19, 2012 17:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save megakorre/2422332 to your computer and use it in GitHub Desktop.
Save megakorre/2422332 to your computer and use it in GitHub Desktop.
;; file core.cljs
(ns ^{:doc ""}
async-kit.core)
(defprotocol Future
(error [this]
"gives you the error associated with this request or nil if no error")
(completed? [this]
"true if request is completed")
(await* [this callback]
"subscribe to the future if it hasent alreddy completed then
executes callback directly"))
(defrecord AsyncFuture [
value ;; atom for value nil if not completed
error-a ;; atom for error nil if no error
subscribers ;; subscribers atom to the future [] default
]
Future
(error [this] @error-a)
(completed? [this] (or @error-a @value))
(await* [this callback]
(cond
(error this) (callback (error this) nil)
@value-state (callback nil @value)
:else (swap! subscribers conj callback))))
(defn async* [callback]
(let [value-delivered (atom false)
future (new AsyncFuture (atom nil) (atom nil) (atom []))
value-cb
(fn [v]
(cond
(error future) (throw "exception: AsyncFuture recived a value after alreddy thrown a error")
@value-delivered (throw "exception: tried to deliver value multiple times to a AsyncFuture")
:else (do
(reset! value-delivered true)
(reset! (:value future) v)
(doseq [sub @(:subscribers future)]
(sub nil v))))
error-cb
(fn [e]
(cond
@value-delivered (throw "error thrown on alreddy delivered future")
:else (do (reset! (:error-a future) e)
(doseq [sub @(:subscribers future)]
(sub e nil)))]
(callback value-cb error-cb)
future))
;; file macros.clj
(ns async-kit.macros)
(defmacro async [bindings & body]
`(async-kit/async*
(fn ~bindings ~@body)))
(defn make-bindings
[remaining-bindings body
completed-sym error-sym]
(cond
(= (count remaining-bindings) 1)
(throw "wrong number of bindings in let-async needs to be event")
(= (count remaining-bindings) 0)
`(~completed-sym (do ~@body))
:else
(let [[binding-name binding-expression & rest]
remaining-bindings]
(cond
(= (first (name binding-name) "-"))
`(let [~binding-name ~binding-expression]
~(make-bindings rest body completed-sym error-sym)))
(= binding-name :if-error)
(let [new-error-sym (gen-sym "error")]
`(let [~new-error-sym (fn ~(first binding-expression)
(~completed-sym (do ~@(rest binding-expression))))]
~(make-bindings rest body completed-sym new-error-sym))))
:else
`(async-kit/await*
~binding-expression
(fn [error# value#]
(if error#
(~error-sym error#)
(let [~binding-name value#]
~(make-bindings rest body completed-sym error-sym))))))))
(defmacro let-async [bindings & body]
(let [complete-sym (gen-sym "completed")
error-sym (gen-sym "error")]
´(async-kit/async*
(fn [~complete-sym ~error-sym]
~(make-bindings bindings body complete-sym error-sym)))
(comment
;; some sudo code
(let-async [some-site (http :GET "/something")
:let some-normal-exp (process some-site)
some-data (-> parse-site :user-id (xhr "/something")]
(render-something "some-template" some-data)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment