2

I have some long running process that returns a core.async channel with the result on it when the process has finished.

Now I'd like to return that result using long-polling with HTTP-kit. Unfortunately I'm a bit confused what the right way of doing so is.

Currently I have a handler (hooked up to a route) that initiates the processing call and sends the result when done:

(defn handler
  [request]
  (let [c (process request)] ;; long running process that returns a channel
    (http/with-channel request channel
      (http/send! channel {:status 200
                           :body (<!! (go (<! c)))))
      (http/on-close channel (fn [_] (async/close! c))))))

It works, but I'm unsure if this is the right way.

EDIT since <!! is blocking I'm now trying a non-blocking variant in a go-loop

(defn handler
  [request]
  (let [c (process request)]
    (http/with-channel request channel
      (async/go-loop [v (<! c)]
        (http/send! channel {:status 200
                             :body v}))
      (http/on-close channel (fn [_] (async/close! c))))))
JoelKuiper
  • 4,362
  • 2
  • 22
  • 33

1 Answers1

1

Why not send on the channel in the go block?

(http/with-channel request channel
  (go (http/send! channel (<! c))))

<!! is blocking - so there is no real advantage in your code from just directly calling <!! c in the handler:

(defn handler [request] (let [c (process request)] ;; long running process that returns a channel {:status 200 :body (<!! c)}))


Edit in response to question update: The updated code works - this is a fully functioning namespace which works for me:

(ns async-resp.core
  (:require [org.httpkit.server :as http]
            [clojure.core.async :as async :refer (<! >! go chan go-loop close! timeout)]))

(defn process
  [_]
  (let [ch (chan)]
    (go
      (<! (timeout 5000))
      (>! ch "TEST"))
    ch))

(defn test-handler
  [request]
  (let [c (process request)]
    (http/with-channel request channel
      (go-loop [v (<! c)]
        (http/send! channel {:status 200
                             :body v}))
      (http/on-close channel (fn [_] (close! c))))))

(defn run
  []
  (http/run-server test-handler {}))

As of the current moment in time though, I had to manually add the tools.analyzer.jvm dependency to project.clj - as I get compilation failures using core.async as-is.

Check you're running the latest core.async and analyzer?

Unk
  • 1,103
  • 6
  • 11
  • Well I need a specific field from the channel so the code looks like (go (http/send! {:status 200 :body (:field (<! c))})) but that gives a exception: https://gist.github.com/joelkuiper/d6c03488432f3146d72b#file-gistfile2-txt – JoelKuiper Jul 03 '14 at 09:32
  • Your gist's code works for me in the latest core.async *provided I also include the latest tools.analyzer.jvm* in my project.clj. – Unk Jul 03 '14 at 11:01
  • Yes it does work! Thanks :-) But the is it the right way of doing something like this? In terms of performance and core.async + http-kit philosophy? – JoelKuiper Jul 03 '14 at 16:23