|
64 | 64 | (cache-remove! (-query-v sub) sub) |
65 | 65 | (-dispose! sub)) |
66 | 66 |
|
| 67 | +(defn- ui-sub? |
| 68 | + "UI subscriptions are batched together and invoked after all subs |
| 69 | + finish computing their new values. This is done to reduce renders and |
| 70 | + prevent consumers from having an inconsistent view of the DB." |
| 71 | + [sub-key] |
| 72 | + (some? (::index sub-key))) |
| 73 | + |
67 | 74 | ;; TODO This could be changed to support "garbage collection": Don't dispose |
68 | 75 | ;; right away, but keep subscriptions around for a while in case they are |
69 | 76 | ;; requested again. |
|
78 | 85 |
|
79 | 86 | (defonce ^:private listeners-state |
80 | 87 | (letfn [(comparator [a b] |
81 | | - (compare (:index a) (:index b)))] |
| 88 | + (compare (::index a) (::index b)))] |
82 | 89 | (atom {:counter 0 :pending (sorted-map-by comparator)}))) |
83 | 90 |
|
84 | 91 | (defn- invoke-listener |
|
103 | 110 |
|
104 | 111 | (interop/next-tick |
105 | 112 | (fn [] |
106 | | - (let [listener-fns (atom nil)] |
107 | | - (swap! listeners-state (fn [state] |
108 | | - (let [{:keys [counter pending] :as new-state} |
109 | | - (update state :counter dec)] |
110 | | - (if (zero? counter) |
111 | | - (do (reset! listener-fns pending) |
112 | | - (update new-state :pending empty)) |
113 | | - new-state)))) |
114 | | - (doseq [[_ f] @listener-fns] |
115 | | - (f))))))) |
| 113 | + (let [{:keys [counter pending]} |
| 114 | + (swap! listeners-state update :counter dec)] |
| 115 | + |
| 116 | + (when (zero? counter) |
| 117 | + (doseq [[listener-key _] pending] |
| 118 | + ;; Triggering a listener-fn can result in a subsequent sub's |
| 119 | + ;; remove-listener to be called (which will remove it from pending). |
| 120 | + ;; This check ensure it's still pending. |
| 121 | + (let [listener-fn (atom nil)] |
| 122 | + (swap! listeners-state (fn [state] |
| 123 | + (reset! listener-fn (get-in state [:pending listener-key])) |
| 124 | + (update state :pending dissoc listener-key))) |
| 125 | + (when-let [f @listener-fn] |
| 126 | + (f)))))))))) |
116 | 127 |
|
117 | 128 | (deftype Listeners [^:mutable listeners] |
118 | 129 | Object |
|
160 | 171 | (-add-listener [_ k f] |
161 | 172 | (.add listeners k f)) |
162 | 173 | (-remove-listener [this k] |
| 174 | + (when (ui-sub? k) |
| 175 | + (swap! listeners-state update :pending dissoc k)) |
163 | 176 | (.remove listeners k) |
164 | 177 | (when (.empty? listeners) |
165 | 178 | (sub-orphaned this))) |
|
238 | 251 | (-add-listener [_ k f] |
239 | 252 | (.add listeners k f)) |
240 | 253 | (-remove-listener [this k] |
| 254 | + (when (ui-sub? k) |
| 255 | + (swap! listeners-state update :pending dissoc k)) |
241 | 256 | (.remove listeners k) |
242 | 257 | (when (.empty? listeners) |
243 | 258 | (sub-orphaned this))) |
|
0 commit comments