Qwerp-Derp
Qwerp-Derp

Reputation: 487

How to use `setTimeout` in ClojureScript?

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?

Upvotes: 8

Views: 8208

Answers (3)

Aliaksandr Sushkevich
Aliaksandr Sushkevich

Reputation: 12364

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")

Upvotes: 0

Asher
Asher

Reputation: 1267

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!"]])

Upvotes: 10

Piotrek Bzdyl
Piotrek Bzdyl

Reputation: 13175

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)

Upvotes: 16

Related Questions