15

I must be missing something very obvious here but I'm trying to setup a very basic program to put an item onto a channel then block until I can take it off again. The entire program is below:

(ns shopping-2.core
  (:require [cljs.core.async :as async :refer [>!! <!! put! chan <! close! <!!]]))

(let [c (chan)]
  (>!! c "hello")
  (.write js/document (<!! c))
  (close! c))

The JavaScript error I'm getting is:

Uncaught TypeError: Cannot call method 'call' of undefined 

I had that error before when I forgot to :refer chan in (if I just open the channel, then close it again the program runs fine)

However this code seems to choke when I want to use the <!! or >!! macros.

Samuel
  • 2,331
  • 1
  • 22
  • 40

2 Answers2

35

There are some differences on what's available in clojurescript from the clojure version of core.async.

Because clojure on the JVM has real threads, it makes available both concurrency patterns with real threads and with go blocks:

  1. Real threads use the macro thread to enclose the core.async magic and its concurrency macros and functions end with two bangs, like <!!, >!!, alt!! and alts!!.

  2. Inversion of control threads (fake threads) use the go macro to enclose the core.async magic and uses the functions with one bang at the end, like <!, >!, alt! and alts!.

In clojurescript (which runs in js) there are no real threads, so only the IoC (inversion of control) threads are available, which means that you have to use the second variant of the concurrency constructs.

Your example would be like:

(ns shopping-2.core
  (:require-macros [cljs.core.async.macros :refer [go]])
  (:require [cljs.core.async :as async :refer [put! chan <! >! close!]]))

(go
  (let [c (chan)]
    (>! c "hello")
    (.write js/document (<! c))
    (close! c)))

Anyway, that example has a concurrency problem, since the <! >! functions are blocking and you are putting into a chan in the same routine, the routine will block on the (>! c "hello") instruction and it will never read, definitely starving your program.

You could fix this using the put! fn which puts without blocking, or efectively running those instructions in different routines which I think demonstrates better what you intended to do.

(ns shopping-2.core
  (:require-macros [cljs.core.async.macros :refer [go]])
  (:require [cljs.core.async :as async :refer [put! chan <! >! close!]]))

;; put! version
(go
  (let [c (chan)]
    (put! c "hello")
    (.write js/document (<! c))
    (close! c)))


;; Concurrent version
;; 2 *threads* concurrently running. One is the putter and the other is the
;; reader
(let [c (chan)]
  (go
    (.write js/document (<! c))
    (close! c))
  (go
    (>! c "hello")))

In the concurrent threads version, you will see that even the code that runs first is the read, it is effectively another routine so the code that runs later (the >!) effectively runs unblocking the first routine.

You can think of the go macro as spawning a new thread that will eventually start executing concurrently and that returns control to the next instructions of code after it immediately.

I suggest reading the code walk-through ignoring the clojure specific parts (>!! <!! etc) and some of the swannodette's tutorials which are great (like Clojurescript 101 and Communicating Sequential Processes)

Joaquin
  • 2,714
  • 20
  • 19
  • Thank you so much for the detailed explanation, a simple "Clojure script does not include the blocking macros" would have sufficed but this is awesome! I have actually run through the Clojurescript 101 tutorial and I was following the walkthrough which is what got me stuck (line 30 of the walkthrough introduces the blocking macros which is what I was trying to figure out!) – Samuel Jan 28 '14 at 09:32
  • 1
    You're welcome. I tried to explain it clear since I've been dabbling with core.async from not long ago and it was a bit hard to get properly at the beginning. The difference from the clojure and clojurescript version should be somewhere in the core.async repo... – Joaquin Jan 29 '14 at 09:42
  • The code walkthrough shows them using `<!!` outside of a `go`. If you can't use `<!!`, what can you use instead? `(take! (go (<! c)))` doesn't do anything https://github.com/clojure/core.async/blob/master/examples/walkthrough.clj#L55 – TankorSmash Sep 05 '21 at 21:28
3

The ClojureScript version of core.async doesn't include <!! or >!!.

I couldn't find a source for this besides the actual source: https://github.com/clojure/core.async/blob/56ded53243e1ef32aec71715b1bfb2b85fdbdb6e/src/main/clojure/cljs/core/async.cljs

ponzao
  • 20,684
  • 3
  • 41
  • 58