Hands-On Reactive Programming with Clojure
上QQ阅读APP看书,第一时间看更新

Making it reactive

As fun as this has been so far, the animation we have created isn't really reactive. Sure, it does react to time itself, but that is the very nature of animation. As we will see later, Reactive Programming is called as such because programs react to external inputs such as a mouse or network events.

We will, therefore, update the animation so that the user is in control of when the color switch occurs: the wave will start off red and switch to blue when the user clicks anywhere within the canvas area. Further clicks will simply alternate between red and blue.

We start by creating infinite—as per the definition of app-time—streams for our color primitives as follows:

(def red  (.pipe app-time (rx-map (fn [_] "red")))) 
(def blue (.pipe app-time (rx-map (fn [_] "blue")))) 

On their own, red and blue aren't that interesting, as their values don't change. We can think of them as constant streams. They become a lot more interesting when combined with another infinite stream that cycles between them based on user input:

(def rx-concat     js/rxjs.concat) 
(def rx-defer      js/rxjs.defer) 
(def rx-from-event js/rxjs.fromEvent)
(def rx-takeUntil js/rxjs.operators.takeUntil) (def mouse-click (rx-from-event canvas "click")) (def cycle-colour (rx-concat (.pipe red (rx-takeUntil mouse-click)) (rx-defer #(rx-concat (.pipe blue (rx-takeUntil mouse-click)) cycle-colour))))

This is our most complex update so far. If you look closely, you will also notice that cycle-colour is a recursive stream; that is, it is defined in terms of itself.

When we first saw the code of this nature, we took a leap of faith in trying to understand what it does. After a quick read, however, we realized that cycle-colour closely follows how we might have talked about the problem: we will use red until a mouse click occurs, after which, we will use blue until another mouse click occurs. Then, we start the recursion.

The change to our animation loop is minimal:

(-> (js/rxjs.zip sine-wave cycle-colour) 
    (.pipe (rx-take 700))
    (.subscribe (fn [[{:keys [x y]} colour]] 
                  (fill-rect x y colour)))) 

The purpose of this book is to help you develop the instinct required to model problems in the way that's demonstrated here. After each chapter, more and more of this example will make sense. Additionally, a number of frameworks will be used both in ClojureScript and Clojure to give you a wide range of tools to choose from.

Before we move on to that, we must take a little detour and understand how we got here.