I have this loop handling animations for a character -- set-image! just takes a key and displays the appropriate image.
(defn main-animation-loop []
(go (while true
(set-image! :normal)
(<! (timeout 8000))
(set-image! :blink)
(<! (timeout 150)))))
Every once in a while the character needs to do some special actions. This should be able to interrupt the main animation:
(defn dance! []
(go (set-image! :look-left)
(<! (timeout 1000))
(set-image! :look-right)
(<! (timeout 1000))
(set-image! :wave)
(<! (timeout 2000))))
What's a good way to pause the main animation while the dance routine is happening?
It's common in CSP style programming to pass a control channel to event loops so you can, at the very least tell them when to stop. In this case if there was a control channel that went to main-animation-loop and you gave a copy of that to dance!, then dance could tell main-animation-loop to pause and unpause appropriatly. Or stop it then start it again (passing the same control channel in case others are using it).
I use a pattern like this to check for messages each time around the event loop:
(go (while (not= control-chan
(second (async/alts! [control-chan (async/timeout arg)])))
(do-stuff-here))
This checks to see if it was the timeout or the control channel that caused the interruption.
(defn main-animation-loop [control-chan]
(go (while (not= control-chan
(second (async/alts! [control-chan (async/timeout 150)])))
(set-image! :normal)
(<! (timeout 8000))
(set-image! :blink))))
(defn dance! [control-chan]
(go (!> control-chan :stop)
(set-image! :look-left)
(<! (timeout 1000))
(set-image! :look-right)
(<! (timeout 1000))
(set-image! :wave)
(<! (timeout 2000))
(main-animation-loop control-chan)))
By passing the same channel to both functions, you allow them to communicate with each other. main-animation-loop will keep looping as long as all the messages it receives are coming from the timeout channel. As soon as it sees that one of them came from the control channel instead of the timeout, it will stop. This allows dance! to tell it to stop by sending any message to the control channel. In similar code I often have the main event loop check the content of the message and do more than just stop, though in this case stopping is enough because dance! can simply start the event loop again.
Related
I'm quite confused about why sente is sending the client a message all on its own around 8 seconds after the page is initially loaded. What else is strange, is that if I quickly send a message to the server over the websocket before this happens, everything stabilizes and the view doesn't crash.
For reference, the front-end is clojurescript with reagent, and its a luminus project. For further reference, its pretty much exactly the sample application from chapter 5 of "web development with clojure".
I can tell that its the server pushing a message to the client that's causing the problem, I just don't know enough about Sente to understand why it would even be doing this.
Here's what I think the relevant code is:
Server side:
(defn save-message! [message]
(if-let [errors (validate-message message)]
{:errors errors}
(do
(db/save-message! message)
message)))
(defn handle-message! [{:keys [id client-id ?data]}]
(when (= id :guestbook/add-message)
(let [response (-> ?data
(assoc :timestamp (java.util.Date.))
save-message!)]
(if (:errors response)
(chsk-send! client-id [:guestbook/error response])
(doseq [uid (:any #connected-uids)]
(chsk-send! uid [:guestbook/add-message response]))))))
Client side (with reagent):
(defn response-handler [messages fields errors]
(fn [{[_ message] :?data}]
(if-let [response-errors (:errors message)]
(reset! errors response-errors)
(do
;; Fires right before the view crashes!
(.log js/console "response-handled")
(reset! errors nil)
(reset! fields nil)
(swap! messages conj message)))))
(defn home []
(let [messages (atom nil)
fields (atom nil)
errors (atom nil)]
(ws/start-router! (response-handler messages fields errors))
(get-messages messages)
(fn []
[:div
[:div.row
[:div.span12
[message-list messages]]]
[:div.row
[:div.span12
[message-form fields errors]]]])))
The problem is that when sente sends the message on its own there is no data to update the messages (or atleast that's my best guess), so the atom's fields become null and reagent (react.js) throws trying to diff and patch from the vdom.
If anyone knows what sente is doing it would be very much appreciated. This exact same set up works fine when you use Immutant's async socket support, and do a lot of the work yourself (serialize/deserialize, handle connections etc).
;;;;;;;;
As a follow up, I solved the issue by filtering for non nil messages:
(defn response-handler [messages fields errors]
(fn [{[_ message] :?data}]
(if-let [response-errors (:errors message)]
(reset! errors response-errors)
(when (not= message nil)
(reset! errors nil)
(reset! fields nil)
(swap! messages conj message)))))
Still, this is kind of a bandage solution, it would be nice to know why Sente throws a message at me after the page loads if the socket isn't used immediately.
You can inspect what is happening by looking at the network tab in the developer tools; there should be a sub-tab for websocket frames.
Sente sends some events on its own, and the event name (a keyword which is the first element of the event vector) is in the chsk namespace if I remember correctly. I believe that you should use some kind of dispatch on the event names anyway, and not assume that only one kind of event will arrive.
In the context of re-frame, I have been filtering the unwanted events and dispatching the rest to the re-frame event loop. I guess that you could do something similar in luminus. On the server side, I have been using multimethods in a similar setting.
When running an XCTest of an asynchronous operation, calling XCTFail() does not immediately fail the test, which was my expectation. Instead, whatever timeout period is remaining from the call to wait is first exhausted, which unnecessarily extends the test time and also creates a confusing failure message implying the test failed due to timeout, when in fact it explicitly failed.
func testFoo() {
let x = expectation(description: "foo")
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
XCTFail("bar")
}
wait(for: [x], timeout: 5)
}
In the above example, although the the failure occurs after roughly 2 seconds, the test does not complete until the timeout period of 5 seconds has elapsed. When I first noticed this behavior I thought I was doing something wrong, but this seems to just be the way it works, at least with the current version of Xcode (9.2).
Since I didn't find any mention of this via google or stackoverflow searches, I'm sharing a workaround I found.
I discovered that the XCTestExpectation can still be fulfilled after calling XCTFail(), which does not count as a pass and immediately expires the wait. So, applying this to my initial example:
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
XCTFail("bar")
x.fulfill()
}
This may be what Apple expects but it wasn't intuitive to me and I couldn't find it documented anywhere. So hopefully this saves someone else the time that I spent confused.
I have a very simple use case: in a text area, I want to stop the event propagation for some key codes and trigger some function f, and for other key codes, I just trigger some function g.
So I've defined a channel called out and a listener, which is putting all keypress events to the channel:
(events/listen
(dom/getElement "input")
"keypress"
(fn [e] (put! out e)))
And here comes the crux: where should I stop the event propagation? To do it after I extracted the event from the channel seems logically to late and, as expected, doesn't work. To stop the event propagation within the handler as shown above seems to be idiomatically wrong. And I really don't want to put any logic into the pre-channel handlers: why would I use core.async then, if I do all the work in the listener directly?
So how is it supposed to work then?
I think #edbond's answer above is correct, and if you have versions of clojure, clojurescript and core.async that are up to date enough to support transducers, you should go with that.
If you are on an older stack, you can use the filter> fn to apply filtering on the channel:
(def ch (async/filter> even? (async/chan)))
(go-loop [msg (async/<! ch)]
(when msg
(println msg)
(recur (async/<! ch))))
(go
(async/put! ch 1) ;; does nothing
(async/put! ch 2)) ;; prints 2
The entrance point for my clojure app is -main, but is there a function that is called upon the application's exit? I need to close some connections when my Heroku dyno goes down, but can't figure out where or how to do that.
As Diego Basch said, use a JVM shutdown hook.
This is what I usually do when I need to do so:
(defn -main
[& args]
((start ...)
(.. (Runtime/getRuntime) (addShutdownHook (proxy [Thread] []
(run []
(stop ...)))))))
I have a program and one func of it takes too long, say longfunc (the specific thing done in the longfunc is a bunch of files generated). so I want to use thread asynchronously executed this longfunc. However, when I wrote like the following, it does not really execute longfunc since there are no files generated...
(thread
(λ ()
(longfunc arg1 arg2)))
and when I get the above thread descriptor, testing whether the thread is running, it says true, this is so weird...
Can anybody explain what's going on with the thread? Is it because of some inner scheduling scheme? how to make the thread run normally?
The thread should start and run normally. For instance, running this program
#lang racket
(thread (lambda ()
(for ([i 10])
(sleep 2)
(printf "thread 1\n"))))
(thread (lambda ()
(for ([i 20])
(sleep 1)
(printf "thread 2\n"))))
should show you both threads running at once.