When an exception bubbles out of the router loop, it now restarts itself.

Previously the  the router loop was permanently broken by an exception.
This change makes figwheel debugging work much more nicely in some circumstances.
Plus it means that in production, apps have a chance at recovery from UHE
This commit is contained in:
mike-thompson-day8 2015-05-02 10:52:11 +10:00
parent b921fa497d
commit 25ed816603
1 changed files with 35 additions and 8 deletions

View File

@ -3,7 +3,7 @@
(:require-macros [cljs.core.async.macros :refer [go-loop go]])
(:require [reagent.core :refer [flush]]
[re-frame.handlers :refer [handle]]
[re-frame.utils :refer [warn]]
[re-frame.utils :refer [warn error]]
[cljs.core.async :refer [chan put! <! timeout]]))
;; -- The Event Conveyor Belt --------------------------------------------------------------------
@ -13,6 +13,12 @@
;;
(def ^:private event-chan (chan)) ;; TODO: set buffer size?
(defn purge-chan
"read all pending events from the channel and drop them on the floor"
[]
#_(loop [] ;; TODO commented out until poll! is a part of the core.asyc API
(when (go (poll! event-chan)) ;; progress: https://github.com/clojure/core.async/commit/d8047c0b0ec13788c1092f579f03733ee635c493
(recur))))
;; -- router loop ---------------------------------------------------------------------------------
;;
@ -27,14 +33,35 @@
;; Example usage (notice the ":flush-dom" metadata):
;; (dispatch ^:flush-dom [:event-id other params])
;;
(go-loop []
(let [event-v (<! event-chan) ;; wait for an event
_ (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.
(<! (timeout 0)))] ;; just in case we are handling one dispatch after an other, give the browser back control to do its stuff
(handle event-v)
(defn router-loop
[]
(go-loop []
(let [event-v (<! event-chan) ;; wait for an event
_ (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.
(<! (timeout 0)))] ;; just in case we are handling one dispatch after an other, give the browser back control to do its stuff
(try
(handle event-v)
;; Unhandled exceptions from event handlers must be managed as follows:
;; - call the standard logging function "error"
;; - allow them to continue to bubble up because the app, in production,
;; may have hooked window.onerror and perform special processing.
;; - But an exception which bubbles out will break the enclosing go-loop.
;; So we'll need to start another one.
(catch js/Object e
(do
;; try to recover from this (probably uncaught) error as best we can
(purge-chan) ;; get rid of any pending events
(router-loop) ;; Exception throw will cause termination of go-loop. So, start another.
(throw e))))) ;; re-throw so the rest of the app's infrastructure (window.onerror?) gets told
(recur)))
;; start event processing
(router-loop)
;; -- dispatch ------------------------------------------------------------------------------------
@ -46,7 +73,7 @@
"
[event-v]
(if (nil? event-v)
(warn "re-frame: \"dispatch\" is ignoring a nil event.") ;; nil would close the channel
(error "re-frame: \"dispatch\" is ignoring a nil event.") ;; nil would close the channel
(put! event-chan event-v))
nil) ;; Ensure nil return. See https://github.com/Day8/re-frame/wiki/Beware-Returning-False