 ;; 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 ------------------------------------------------------------------------------------

  "
  [event-v]
  (if (nil? event-v)
    (warn "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


;;
(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 ---------------------------------------------------------------------------------
;;
