Skip to content

Instantly share code, notes, and snippets.

@samn
Last active December 25, 2015 17:39
Show Gist options
  • Save samn/7015194 to your computer and use it in GitHub Desktop.
Save samn/7015194 to your computer and use it in GitHub Desktop.
This gist describes a technique for asynchronously fetching values from an external resource. This fetch may be expensive so we don't want to block the calling thread. Using a hash-set and watcher decouples the fetch of the value from the request for the value without callbacks. Looking at the difference of the before and after set values in the…
(def fetch-set (ref (hash-set)))
(def resource-map (ref {}))
(defn fetch-resource
[resource-key]
;; perform some expensive operation that returns a value
)
(defn request-fetch
"Requests an asynchronous fetch of some resource."
[fetch-set resource-key]
(dosync
(commute fetch-set conj resource-key)
;; return nil to indicate that a fetch was requested, the value isn't yet present
nil))
(defn fetch-set-watcher
"Asynchronously fetch the resources that have been added to the watched set."
[_ _ old-set new-set]
(doseq [resource-key (clojure.set/difference new-set old-set)]
(future
(let [resource (fetch-resource resource-key)]
(dosync
(when-not (get @resource-map resource-key)
;; alter so the view on resource-map is consistent between
;; the when-not and alter. Don't want to clobber an existing
;; value in the resource-map if it was added concurrently.
(alter resource-map assoc resource-key resource))
;; This removal will trigger the watcher again but will
;; be ignored because of the set difference.
(commute fetch-set disj resource-key))))))
(add-watch fetch-set :async-fetch fetch-set-watcher)
;; Then deref & read resource-map to access the asynchronously fetched values
(defn get-resource
[resource-key]
(if-let [existing-resource (get @resource-map resource-key)]
existing-resource
;; request-fetch returns nil, which lets a caller easily check
;; if the resource was returned.
(request-fetch resource-key)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment