Which Backbone.Events pattern keeps model-change alerts useful without flooding the UI?
0 reputation · 08 Aug 2021, 06:08 UTC
0 reputation · 08 Aug 2021, 06:08 UTC
I'm designing an alert layer for a Backbone.js application where several models and collections fire change events rapidly (live form validation plus polling sync). I want users to see meaningful notifications without a toast appearing on every keystroke or every poll cycle.
Two documented building blocks seem relevant: a central dispatcher built with _.extend({}, Backbone.Events) so alerts are decoupled from individual models, and wrapping handlers with _.throttle or _.debounce so bursts collapse into one notification. Assume Backbone 1.x with Underscore available.
My uncertainty is how these compose. If I throttle at each model listener, different models still alert independently and the UI can flood. If I throttle only at the dispatcher's single alert event, a slow-but-steady stream of distinct issues may get swallowed, and an aggressively long interval could hide legitimate rapid validation feedback.
Which layer should own the rate-limiting: per-model listeners, the central dispatcher, or both with different intervals?
Is there a conventional way to distinguish 'important, alert immediately' events from 'noisy, coalesce these' events through one dispatcher without string-matching event names everywhere?
Are there known pitfalls with off() unsubscription when the subscribed handler is a throttled wrapper rather than the original function?
35275 reputation · 08 Aug 2021, 16:35 UTC
Rate-limit at the central dispatcher, not at individual model listeners. Keep per-model listeners thin: they only translate a model change into a hub event. Attach a single debounced/throttled handler to the hub for normal alerts and keep a separate immediate path for urgent alerts. That coalesces bursts from any model or collection into one UI notification while still allowing important events to surface immediately.
_.extend({}, Backbone.Events) creates a plain event hub. Models publish via hub.trigger and UI code subscribes once via hub.on._.debounce and _.throttle collapse rapid calls into one invocation. Wrapping the UI handler, not each model listener, collapses bursts across all sources.off() matches by reference. If you subscribe with a wrapper created by _.debounce / _.throttle, you must keep the wrapper reference and pass the same reference to off(), otherwise the subscription remains active.Per-model throttling still allows N models to each emit independently, so the UI can flood. Throttling only at the dispatcher coalesces across sources, which is the intended behavior for a global toast layer. A single debounced handler will still show the most recent payload after the burst ends.
Assumption: your alert UI can be updated with the latest payload rather than needing every intermediate state. If you truly need every intermediate validation message, debounce is the wrong primitive.
Use one dispatcher with two channels or a payload flag, not per-model name checks.
var hub = _.extend({}, Backbone.Events);model.on('change', function(){ hub.trigger('alert', {msg:..., urgent:false}); });var showToast = function(payload){ /* render */ };
var debouncedShow = _.debounce(showToast, 250);
hub.on('alert', function(payload){
if(payload.urgent){ showToast(payload); return; }
debouncedShow(payload);
});Urgent events bypass the debounce. The flag is set at publish time, so publishers decide importance without the dispatcher inspecting event names.
Alternative: publish to two event names, alert:urgent and alert, and bind separate handlers. The payload flag avoids scattering string checks.
Store the wrapper. Wrong:
hub.on('alert', _.debounce(showToast, 250));
// later
hub.off('alert', _.debounce(showToast, 250)); // new wrapper, no matchCorrect:
var debouncedShow = _.debounce(showToast, 250);
hub.on('alert', debouncedShow);
// later
hub.off('alert', debouncedShow);Do not nest multiple throttle/debounce wrappers on the same handler; double-wrapping makes timing unpredictable and complicates off().
urgent flag in the payload.To tune the recommendation, the acceptable latency for validation feedback would change the debounce interval choice. If you can share the target responsiveness for non-urgent toasts, the interval can be set more precisely.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.