53

After using backbone for a couple of weeks I have realized that underscore defer ended up fixing many of the async issues I ran into regarding rendering various views. Can someone please help me understand exactly what underscore defer does and how is it different that $.ready() or other type of wait for dom to render functions. What are the down sides of using it ?

_.defer = function(func) {
    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
alex
  • 479,566
  • 201
  • 878
  • 984
cesara
  • 842
  • 1
  • 7
  • 19
  • Well, what does `_.delay` do? Therein lies the answer. –  Mar 19 '13 at 00:04
  • it seems to wait for 1 millisecond. I still don't understand how that knows that certain elements are rendered etc. maybe I am using it incorrectly? – cesara Mar 19 '13 at 00:06
  • It may be more helpful if you post an example of a place you had to use `defer` and don't understand why. – Alex Wayne Mar 19 '13 at 00:13
  • 3
    This got to be my one of my favourite questions on SO. 'Sheesh this solves all my problem but I don't have a clue' :D –  Mar 19 '13 at 08:01
  • I've noticed that `_.defer` can cause memory leaks in my Backbone app. So while it may fix rendering issues, it can add memory ones. Replacing `_.defer(_.bind(this.renderViews, this));` with `this.renderViews();` halved the amount heap profiles grew with navigation in my single page application. I think it is because it adds the possibility of screwing up the dereferencing of resources for when views close by doing work outside of the natural order. This obviously depends on how you use Backbone in the first place of course. – Iolo Jul 11 '14 at 11:19

1 Answers1

77
# These are equivalent
_.defer(fn);
setTimeout(fn, 1);

So defer is simply a one millisecond setTimeout. (It's got a few more convenience features but those aren't important here.)


JavaScript has run loops. It's single threaded, but its execution starts and stops based on events or timers. Each time your JS engine kicks on to run some code, it's starting one iteration of its run loop.

So what defer does is say "run this code in the next run loop".

_.defer(function() { alert('after'); });
alert('before');

This alerts "before" and then "after". This is because the the current run loop concludes which alerts "before", and then right afterward a new run loop starts and runs the code the alerts "after".

So anytime you have code right here, but you want it to run code which occurs after this code first, then you would use defer.

_.defer(functionToRunLast);
functionToRunFirst();

This can be handy with the DOM. Sometimes you change it, but the changes don't parse or render immediately. At the end of the run loop, the browser catches up and parses and renders the DOM, then the next run loop starts and can interact with the newly rendered DOM.

(Exactly what scenarios cause this delayed DOM parse, I'm not sure, but I've noticed it in my own projects in the past.)


It is NOT a replacement for DOM ready. The next run loop may happen before DOM ready ever fires, don't confuse these concepts.

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • 3
    I'm pretty sure the DOM will be updated immediately but the rendering (and hence things like sizing and positioning) generally won't happen until after the browser gets control back; this is why you often need a `defer` (or the equivalent `setTimeout`) when you want to bind Google Maps to something that you just added: the map wants to know how big things are but that information isn't available until the browser gets a chance to compute it. – mu is too short Mar 19 '13 at 03:06
  • Thanks Alex that helps a lot! – cesara Mar 19 '13 at 19:58
  • how does _.defer() compare to setTimeout(fn, 0) ?? – Jeff Voss Feb 19 '14 at 19:01
  • 2
    @whiteb0x It's basically the same thing. Checkout out the [underscore source](http://underscorejs.org/docs/underscore.html#section-70) and you'll see that `defer` call's `delay` with a time of `1`, and `delay` calls `setTimeout`. – Alex Wayne Feb 19 '14 at 19:14
  • 3
    Your answer is correct, although what you're calling "loops" is technically called "messages." The messages are added to a queue. The event loop processes each of the messages, one at a time. When you call setTimeout, it puts the callback at the end of the queue. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop – Patrick McElhaney May 21 '14 at 20:36
  • 1
    @PatrickMcElhaney The link to EventLoop on MDN was very helpful. – gfullam Jan 20 '15 at 18:43
  • 1
    Does anyone know if there's a difference/reason underscore sets the timeout to 1, rather than 0? – mAAdhaTTah Jul 30 '15 at 18:10
  • 1
    @mAAdhaTTah discussion on that is here: http://stackoverflow.com/questions/8341803/difference-between-settimeoutfn-0-and-settimeoutfn-1 – Alex Wayne Jul 30 '15 at 19:01