8

I'm trying to make a sleep function in ClojureScript (w/ Reagent):

(ns cljweb.webpage
  (:require [reagent.core :as reagent]))

(def temp-atom (reagent/atom 0))

(defn sleep [msec]
  (js/setTimeout (fn []) msec)) 

(defn page []
  [:div
   [:p @temp-atom]
   [:button
    {:on-click
      (fn []
        (sleep 3000) 
        (swap! temp-atom inc))}
    "Click me!"]])

For some reason, this doesn't sleep properly - when I click the "Click me!" button, temp-atom increments instantly - when I time it, by putting this after in page:

[:p (time (sleep 3000))]

I get this in the console:

"Elapsed time: 0.015000 msecs"

What did I do wrong in the code?

3 Answers 3

16

Javascript's setTimeout function accepts two arguments: function and timeout in milliseconds. Its contract is to run the received function after the timeout passes.

Your code doesn't pass the function you would like to execute after 3 seconds but instead passes a no-op function ((fn [])).

Your sleep function should look like this (and it would be better named timeout or you could just call js/setTimeout directly in your on-click handler):

(defn sleep [f ms]
  (js/setTimeout f ms))

You also need to change how you call this function:

(sleep #(swap! temp-atom inc) 3000)

Or with calling js/setTimeout directly:

(js/setTimeout #(swap! temp-atom inc) 3000)
Sign up to request clarification or add additional context in comments.

3 Comments

Hmmm... is there a way to achieve sleep without using setTimeout? That's my primary goal. I'm pretty sure that make my question an XY question, though, so I may have to post a new question.
What is wrong with setTimeout? Take a look at stackoverflow.com/a/39914235/597473.
Aww... I saw the results of that question, and it's kinda disappointing. In order to actually implement the solution of the newest solution, I have to install another library, which is a bummer. Thanks anyway!
10

With ClojureScript, the best way to write asynchronous code is with the CoreAsync library. In your case, take a look at the timeout function:

(ns cljweb.webpage
  (:use-macros [cljs.core.async.macros :only [go]]
  (:require [reagent.core :as reagent]
            [cljs.core.async :refer [<! timeout]]))

(def temp-atom (reagent/atom 0))

(defn page []
   [:div
     [:p @temp-atom]
     [:button
       {:on-click
         (fn []
          (go
            (<! (timeout 3000))
            (swap! temp-atom inc)))}
         "Click me!"]])

3 Comments

I have to say it's not the best way. There are several ways in the language to deal with asynchronous code. Different tasks require different tools. In this case core.async is an overkill. Also, it's core.async not CoreAsync.
@Nek so what is best way in this use case according to your opinion.
It's setTimeout
0

There is a way to implement such functionality using goog.async.Debouncer

Here is an example:

(ns example.utils
  (:require [goog.async.Debouncer]))

(defn debounce [f interval]
  (let [dbnc (goog.async.Debouncer. f interval)]
    (fn [& args] (.apply (.-fire dbnc) dbnc (to-array args)))))

(defn save-input! [input]
  (js/console.log "Saving input" input))

(def save-input-debounced!
  (debounce save-input! 3000))

(save-input-debounced! "hi")

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.