Use yield instead of (timeout 0)

(timeout 0) resolves to using js/setTimeout which will actually take 4+
msecs in browsers. This is an eternity. The nextTick approach was
proposed by Patrick O'Brien and implemented here.

Docs on nextTick from Closure library:

Fires the provided callbacks as soon as possible after the current JS
execution context. setTimeout(…, 0) takes at least 4ms when called from
within another setTimeout(…, 0) for legacy reasons.

This will not schedule the callback as a microtask (i.e. a task that can
preempt user input or networking callbacks). It is meant to emulate what
setTimeout(_, 0) would do if it were not throttled. If you desire
microtask behavior, use goog.Promise instead.
This commit is contained in:
Daniel Compton 2015-09-27 21:39:35 +13:00
parent 3e8f1e1dfa
commit a2e8c09775
1 changed files with 13 additions and 3 deletions

View File

@ -4,7 +4,8 @@
(:require [reagent.core :refer [flush]] (:require [reagent.core :refer [flush]]
[re-frame.handlers :refer [handle]] [re-frame.handlers :refer [handle]]
[re-frame.utils :refer [warn error]] [re-frame.utils :refer [warn error]]
[cljs.core.async :refer [chan put! <! timeout]])) [cljs.core.async :refer [chan put! <! timeout close!]]
[goog.async.nextTick]))
;; -- The Event Conveyor Belt -------------------------------------------------------------------- ;; -- The Event Conveyor Belt --------------------------------------------------------------------
;; ;;
@ -25,7 +26,7 @@
;; In a perpetual loop, read events from "event-chan", and call the right handler. ;; In a perpetual loop, read events from "event-chan", and call the right handler.
;; ;;
;; Because handlers occupy the CPU, before each event is handled, hand ;; Because handlers occupy the CPU, before each event is handled, hand
;; back control to the browser, via a (<! (timeout 0)) call. ;; back control to the browser, via a (<! (yield)) call.
;; ;;
;; In some cases, we need to pause for an entire animationFrame, to ensure that ;; In some cases, we need to pause for an entire animationFrame, to ensure that
;; the DOM is fully flushed, before then calling a handler known to hog the CPU ;; the DOM is fully flushed, before then calling a handler known to hog the CPU
@ -34,13 +35,22 @@
;; (dispatch ^:flush-dom [:event-id other params]) ;; (dispatch ^:flush-dom [:event-id other params])
;; ;;
(defn yield
"Yields control to the browser. Faster than (timeout 0).
See http://dev.clojure.org/jira/browse/ASYNC-137"
[]
(let [ch (chan)]
(goog.async.nextTick #(close! ch))
ch))
(defn router-loop (defn router-loop
[] []
(go-loop [] (go-loop []
(let [event-v (<! event-chan) ;; wait for an event (let [event-v (<! event-chan) ;; wait for an event
_ (if (:flush-dom (meta event-v)) ;; check the event for metadata _ (if (:flush-dom (meta event-v)) ;; check the event for metadata
(do (flush) (<! (timeout 20))) ;; wait just over one annimation frame (16ms), to rensure all pending GUI work is flushed to the DOM. (do (flush) (<! (timeout 20))) ;; wait just over one annimation frame (16ms), to rensure all pending GUI work is flushed to the DOM.
(<! (timeout 0)))] ;; just in case we are handling one dispatch after an other, give the browser back control to do its stuff (<! (yield)))] ;; just in case we are handling one dispatch after an other, give the browser back control to do its stuff
(try (try
(handle event-v) (handle event-v)