Skip to content

Instantly share code, notes, and snippets.

@pangloss
Last active January 23, 2020 05:45
Show Gist options
  • Save pangloss/6002224 to your computer and use it in GitHub Desktop.
Save pangloss/6002224 to your computer and use it in GitHub Desktop.
ClojureScript XHR with core.async.
(ns xn.test
(:require-macros [cljs.core.async.macros :refer [go alts!]]
(:require [xn.core :as xn]
[xn.util :as u]
[cljs.core.async :refer [<! take!]]
[xn.xhr :refer [request-body request-records chain-req]]))
(def sample-app
(reify
xn/Application
(token [_] "sample 16qqLstdtzyRjWA4zxxE")
(base-url [_] "http://localhost:8080/v1")))
(defn show-req [[r ok err]]
(take! ok #(.log js/console "ok:" (str %)))
(take! err #(.log js/console "err:" (str %))))
(defn g1 []
(show-req (xn/get sample-app :url "/account")))
(defn g2 []
(show-req (chain-req (xn/get sample-app :url "/account") (comp str keys))))
(ns ^{:doc "Make network requests.
Based on:
https://github.com/brentonashworth/one
/blob/934df802a55cf340740cabc29a129a9d29073733
/src/lib/cljs/one/browser/remote.cljs"}
xn.xhr
(:use [xn.util :only [map->obj cancel]]
[cljs.reader :only [read-string]])
(:require [clojure.browser.event :as event]
[goog.net.XhrManager :as manager]
[cljs.core.async :as async :refer [<! >! chan close! take! put!]])
(:require-macros
[cljs.core.async.macros :as m :refer [go]]))
(def method-map {:get "GET" :put "PUT" :post "POST" :patch "PATCH" ; :delete "DELETE"
; domain-specific methods:
:read "GET" :create "PUT" :update "PATCH" :run "POST" :delete "DELETE"})
(def ^:private
*xhr-manager*
(goog.net.XhrManager. nil (js-obj "accept" "text/edn") nil nil 5000))
(defn read-response [response]
(let [body (.getResponseText response)]
(cond
(empty? body) nil
(string? body) (read-string body)
:else body)))
(defn handle-response [body err]
(fn [e]
; Could trigger other global events here if needed...
(if (.isSuccess e/target)
(do
(put! body (read-response e/target))
(close! body)
(close! err))
(do
(put! err (or (read-response e/target) "error"))
(close! body)
(close! err)))))
(defn request-body
"Asynchronously make a network request for the resource at url. The
entry for `:event` contains an instance of the `goog.net.XhrManager.Event`.
Other allowable keyword arguments are `:method`, `:content`, `:headers`,
`:priority`, `:retries`, and `:timeout`. `:method` defaults to \"GET\" and `:retries`
defaults to `0`. `:timout` may be a number of milliseconds or a timeout channel.
Returns [request body err] where body and err are channels"
[url & {:keys [method body headers priority retries id timeout]
:or {method :get
retries 0}}]
(let [body (chan 1) err (chan 1)]
(if-let [req (.send *xhr-manager*
id
url
(method-map method)
body
(map->obj headers)
priority
(handle-response body err)
retries)]
#_(let [timeout (if (number? timeout) (async/timeout timeout) timeout)]
(take! timeout (fn [] (cancel req)))) ; <- should cause a failure response...?
[req body err])))
(defn request-records [& args]
(let [[req body err] (apply request-body args)
records(chan)]
(go
(when-let [data (<! body)]
(if (sequential? data)
(doseq [record data] (>! records record))
(>! records data))
(close! records)))
[req records err]))
(defn chain-req
([req ok-fn]
(chain-req req ok-form identity))
([[req ok err] ok-fn err-fn]
[req
(go (ok-fn (<! ok)))
(go (err-fn (<! err)))]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment