Basically, what I want is to implement this piece of code in ClojureScript:
var win = window.open('foo.html', 'windowName');
var timer = setInterval(function() {
if(win.closed) {
clearInterval(timer);
alert('closed');
}
}, 1000);
I tried this:
(let [popup (.open js/window "foo.html" "windowName")
interval (.setInterval
js/window
(fn []
(when (.-closed popup)
(do
;; 'interval' is undefined at this point
(.clearInterval js/window interval)
(.alert js/window 'closed')))
1000)]
...)
but CLJS compiler gives me a warning that interval is not defined.
Any ideas?
The issue is that you access interval local binding in your anonymous function before that binding has been defined (right hand side has to be evaluated first before it gets bound to interval symbol and until then interval is not defined.
You might workaround it by defining an atom storing your interval and access it from your callback function:
(let [popup (.open js/window 'foo.html', 'windowName')
interval (atom nil)]
(reset! interval (.setInterval
js/window
(fn []
(when (.-closed popup)
(do
(.clearInterval js/window #interval)
(.alert js/window "Closed")))))))
I am not sure if there is a more elegant way to achieve it using your approach with an interval callback.
Use of an atom
is easy
and is included within ClojureScript's core functionality
however interval being an atom may not clearly convey its actual nature and intent.
The original issue was the compiler's complaint that interval was not defined. So what is needed is a value that:
can be defined first and
resolved (delivered) later
That sounds like a promise to me. As of ClojureScript 1.8.34 Clojure promises aren't supported yet. Because of the existence of core.async there are indications that support of Clojure's promise isn't an urgent priority in ClojureScript - especially as core.async contains promise-chan. With promise-chan the code could be written as follows
(ns test.core
(:require-macros [cljs.core.async.macros :refer [go]])
(:require [cljs.core.async :refer [<! close! promise-chan put!]]))
(let [interval (promise-chan)
fooWin (.open js/window "./foo.html", "windowName")
checkOnFooWin
(fn []
(when (.-closed fooWin) ;; when has an implicit do
(go
(.clearInterval js/window (<! interval)))
(.alert js/window "Closed")))]
(put! interval (.setInterval js/window checkOnFooWin 500))
(close! interval))
This code is neither easier nor more elegant than the atom version - however it does have the advantage that
interval can only be resolved once
each and every value taken from interval will always be the initially delivered value.
Another way is to use direct js interop:
(let [popup (.open js/window "foo.html" "windowName")]
(js* "var interval = setInterval(function() {
if (popup.closed) {
clearInterval(interval);
alert('close');
}
}, 500);")
...)
Related
I'm using core.async to do something in parallel, and then using alts!! wait on certain amount of result with timeout.
(ns c
(:require [clojure.core.async :as a]))
(defn async-call-on-vector [v]
(mapv (fn [n]
(a/go (a/<! (a/timeout n)) ; simulate long time work
n))
v))
(defn wait-result-with-timeout [chans num-to-get timeout]
(let [chans-count (count chans)
num-to-get (min num-to-get
chans-count)]
(if (empty? chans)
[]
(let [timeout (a/timeout timeout)]
(loop [result []
met 0]
(if (or (= (count result) num-to-get)
(= met chans-count)) ; all chan has been consumed
result
(let [[v c] (a/alts!! (conj chans timeout))]
(if (= c timeout)
result
(case v
nil (do (println "got nil") (recur result met)) ; close! on that channel
(recur (conj result v) (inc met)))))))))))
and then invoke like:
user=> (-> [1 200 300 400 500] c/async-call-on-vector (c/wait-result-with-timeout 2 30))
this expression will prints out a lot of got nil. It seems channel returned by go block will close that channel after result has been returned. And this will causes alts!! return nil on this case. but this is very CPU unfriendly, it just like busy waiting. Is there a way to avoid this?
I solved this by define a macro like go, but return a channel that will not closed on result returned. Is this a right way to solve it?
I'm using core.async to do something in parallel, and then using alts!! wait on certain amount of result with timeout.
It looks like you want to collect all of the values that will be delivered by some channels, until all of those channels are closed, or until a timeout occurs. One way to do that is to merge those channels onto a single channel, and then use alts! within a go-loop to collect the values into a vector:
(defn wait-result-with-timeout [chans timeout]
(let [all-chans (a/merge chans)
t-out (a/timeout timeout)]
(a/go-loop [vs []]
(let [[v _] (a/alts! [all-chans t-out])]
;; v will be nil if either every channel in
;; `chans` is closed, or if `t-out` fires.
(if (nil? v)
vs
(recur (conj vs v)))))))
It seems channel returned by go block will close that channel after result has been returned.
You are correct, that is the documented behavior of a go block.
I solved this by define a macro like go, but return a channel that will not closed on result returned. Is this a right way to solve it?
Probably not, although it's not for me to say whether it's right or wrong for your particular use case. Generally speaking, channels should close if they are done delivering values, to indicate the semantics of being done delivering values. For example, the above code uses the closing of all-chans to indicate that there is no more work to wait on.
I am follwing the example here http://patternhatch.com/2013/06/12/messaging-using-clojure-and-zeromq/
I have verified that I can serialize MarketData and have built the protobuf for it.
Instead of using chesire serialization I decided to try my new learned protobuf serialization knowledge. When I modified the functions in that example into their gpb versions, when I run
(future-call market-data-publisher-gpb)
It seems ok. However, when I run the client
(get-market-data-gpb 100)
Nothing happens. I have two questions:
1) Is there some sort of graphical or otherwise debugger for Clojure?
2) If someone can point me in the right direction as to what I am doing wrong on my modfied example that would also be helpful.
I seem to remember that over ZMQ with a [protobuf] binary data payload required a different set of calls?
(ns clj-zmq.core
(:import [org.jeromq ZMQ])
)
(use 'flatland.protobuf.core)
(import com.example.Example$MarketData)
(def MarketData (protodef Example$MarketData))
(def ctx (ZMQ/context 1))
(defn market-data-publisher-gpb
[]
(let [s (.socket ctx ZMQ/PUB)
market-data-event (fn []
{:symbol (rand-nth ["CAT" "UTX"])
:size (rand-int 1000)
:price (format "%.2f" (rand 50.0))})]
(.bind s "tcp://127.0.0.1:6666")
(while :true
(.send s ( protobuf-dump(market-data-event))))))
; Client
(defn get-market-data-gpb
[num-events]
(let [s (.socket ctx ZMQ/SUB)]
(.subscribe s "")
(.connect s "tcp://127.0.0.1:6666")
(dotimes [_ num-events]
(println (protobuf-load MarketData (.recv s))))
(.close s)))
Both Eclipse Counterclockwise and IntelliJ Cursive have Clojure debug support.
Also, your address looks bad - should be "tcp://127.0.0.1:6666".
What would be an idiomatic way of executing a function within a time limit? Something like,
(with-timeout 5000
(do-somthing))
Unless do-something returns within 5000 throw an exception or return nil.
EDIT: before someone points it out there is,
clojure (with-timeout ... macro)
but with that the future keeps executing that does not work in my case.
I think you can do this reasonably reliably by using the timeout capability within futures:
(defmacro with-timeout [millis & body]
`(let [future# (future ~#body)]
(try
(.get future# ~millis java.util.concurrent.TimeUnit/MILLISECONDS)
(catch java.util.concurrent.TimeoutException x#
(do
(future-cancel future#)
nil)))))
A bit of experimenting verified that you need to do a future-cancel to stop the future thread from continuing to execute....
What about?
(defn timeout [timeout-ms callback]
(let [fut (future (callback))
ret (deref fut timeout-ms ::timed-out)]
(when (= ret ::timed-out)
(future-cancel fut))
ret))
(timeout 100 #(Thread/sleep 1000))
;=> :user/timed-out
This isn't something you can do 100% reliably on the JVM. The only way to stop something after a while is to give it a new thread, and then send that thread an exception when you want it to stop. But their code can catch the exception, or they can spin up another thread that you don't control, or...
But most of the time, and especially if you control the code that's being timed out, you can do something like we do in clojail:
If you wanted to make that prettier you could define a macro like
(defmacro with-timeout [time & body]
`(thunk-timeout (fn [] ~#body) ~time))
It's a quite a breeze using clojure's channel facilities
https://github.com/clojure/core.async
require respective namespace
(:require [clojure.core.async :refer [>! alts!! timeout chan go]])
the function wait takes a timeout [ms], a function [f] and optional parameters [args]
(defn wait [ms f & args]
(let [c (chan)]
(go (>! c (apply f args)))
(first (alts!! [c (timeout ms)]))))
third line pops off the call to f to another thread. fourth line consumes the result of the function call or (if faster) the timeout.
consider the following example calls
(wait 1000 (fn [] (do (Thread/sleep 100) 2)))
=> 2
but
(wait 50 (fn [] (do (Thread/sleep 100) 2)))
=> nil
You can probably use an agent, and then await-for it.
Adding a possible (macro-less) alternative to the mix (though the macro isn't required in the accepted answer of course)
(defn with-timeout [f ms]
(let [p (promise)
h (future
(deliver p (f)))
t (future
(Thread/sleep ms)
(future-cancel h)
(deliver p nil))]
#p))
Requires two threads, but just an idea.
I'm trying to pull an integer out of localStorage with a simple clojurescript app. Everything I've tried ended up trying has some sort of wrong behavior.
Below is my program without initializing from local storage. I'll ignore the key not found case since I've a JQuery version that handles that to prime the storage. Also, the JQuery app reads the ClojureScript saves to localStorage fine. So that's working for me.
Quick summary is this. I message says "There have been $number days since the last incident" The $number is in a div named "counter." I've got three buttons; one increments the count, one decrements the count, and the last resets the count to zero.
(ns days.core
(:require [goog.events :as events]
[goog.string :as string]
[goog.math.Integer :as int]
[goog.dom :as dom]))
(defn initial-state [] 0)
(def count (atom (initial-state)))
(defn set-counter [n]
(do (.setItem (.localStorage (dom/getWindow)) "count" n)
(dom/setTextContent (dom/getElement "counter") n)))
(defn set-button-fn [button-id f-update]
(events/listen (dom/getElement button-id)
"click"
(fn [] (do (f-update) (set-counter #count)))))
(defn start-app []
(do
(set-counter #count)
(set-button-fn "addDay" (fn [] (swap! count inc)))
(set-button-fn "decDay" (fn [] (swap! count dec)))
(set-button-fn "reset" (fn [] (reset! count 0)))))
(start-app)
When I try to use goog.math.Integer.fromString() to cast to an integer, the call to inc will append a 1 on the end (7 went to 71 and 711). The call to dec will do what I expect, decrement it numerically (711 went to 710 and 709). Here's how I'm trying to initialize that.
(defn initial-state []
(integer/fromString (.getItem (.localStorage (dom/getWindow)) "count")))
I realized this was a goog.math.Integer object so I tried to call .toNumber() on it. But this and .toInt() seemed to give me a function. function (){if(this.e==-1)return-w(this).D();else{for(var a=0,b=1,d=0;d=0?e:Ua+e)b;b=Ua}return a}} to be exact.
(defn initial-state [] (.toNumber
(integer/fromString (.getItem (.localStorage (dom/getWindow)) "count"))))
Clojure seems to use java's Integer class to cast from a string to an int even to the point of having (int "1") throw so that idea was shot.
I also tried to call javascript's parseInt(). This is how I do it in the JQuery version. However the ClojureScript call always returns a 1. Even if my JQuery version stores an 8 as evidenced by Chrome's developer tools.
(defn initial-state []
(.parseInt (dom/getWindow)
(.getItem (.localStorage (dom/getWindow)) "count")))
Any ideas how I can get that string value to behave as an integer? It's got to be simple, but I'm getting nowhere.
You can access the parseInt function via the js namespace like so:
(js/parseInt "7")
Here's a method that works in both Clojure and ClojureScript:
(defn- str->int [s]
#?(:clj (Integer/parseInt s)
:cljs (js/parseInt s)))
(str->int "10")
> 10
The ClojureScript way:
(ns reader-test.core
(:require [cljs.reader :as reader]))
(reader/read-string "7") ;; 7 - integer
(reader/read-string "1.3") ;; 1.3 - non-integer number
This works not only for integers, but for any type.
While preparing an answer to another question, I created one for myself. Consider the following short program.
(ns net.dneclark.JFrameAndTimerDemo
(:import (javax.swing JLabel JButton JPanel JFrame Timer))
(:gen-class))
(defn timer-action [label counter]
(proxy [java.awt.event.ActionListener] []
(actionPerformed
[e]
(.setText label (str "Counter: " (swap! counter inc))))))
(defn timer-fn []
(let [counter (atom 0)
label (JLabel. "Counter: 0")
timer (Timer. 1000 (timer-action label counter))
panel (doto (JPanel.)
(.add label))]
(.start timer)
(doto (JFrame. "Timer App")
(.setContentPane panel)
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(.setLocation 300 300)
(.setSize 200 200)
(.setVisible true))))
(defn -main []
(timer-fn))
In the action listener, 'timer-action', the value of the 'counter' argument is altered. The actual variable is declared in the 'timer-fn' function, but is altered in the listener. In my earlier experience with languages like Pascal, I would have considered 'counter' to be passed by reference. Is that the case here or is this an example of a closure? Something else?
Thanks for the help.
Yes, it's a closure. The lexical context of the handler function definition is preserved, and when it is later invoked it can access and update variables that "live" there.
I'm not sure how to answer the question, "why?" other than to point out that it's simply the way the language is defined to work.