Downshift Your Code

By YUI TeamJuly 9th, 2007

Web browsers have advanced to the point where things happen fairly fast across the board. Events are fired fast, user interactions can be registered fast, code executes fast. All this speed is typically a good thing, as it keeps modern web applications zipping along at a steady clip. But sometimes fast is actually too fast. Sometimes, you simply can’t let something happen multiple times in a second due to complex calculations or major UI shifts that occur.

As a concrete example, consider the resize event. In most browsers, the resize event fires after the user has finished resizing the window, meaning that the event is fired once for each window resize. Internet Explorer, on the other hand, fires the resize event continuously as the user is resizing the browser. If you have anything more than some simple calculations being applied during onresize, it’s possible to end up confusing IE by having too much going on (especially if the event handler does any UI manipulations). The solution is to throttle your code so that a method is called a maximum number of times per second. This can be achieved using timeouts and a little bit of indirection. The basic pattern is as follows:


YAHOO.namespace("example");

YAHOO.example.MyObject = {

    _timeoutId : 0,
    _process : function () {
        //processing code goes here
    },

    process : function () {
        clearTimeout(this._timeoutId);
        var me = this;
        this._timeoutId = setTimeout(function(){
            me._process();
        }, 250);
    }
};

This object contains two “private” members, _timeoutId and _process(), which should not be accessed from outside of the object (if you want to make them truly private, you can use Crockford’s Module Pattern). The _timeoutId property stores a timeout ID that is used to control how often _process(), the method doing all the processing, is called. The process() method is the one that should be called externally because it controls how often the processing can occur. The first step in this method is to clear the timeout represented by _timeoutId, then, it creates a new timeout to call _process(). This sequence makes it impossible for _process() to be called more frequently than every 250 milliseconds while assuring that _process() will be called no later than 250 milliseconds after the last call to process(). The maximum number of times _process() can be called is four times a second (using 250 milliseconds as the timeout period). You can, of course, adapt this time depending on your particular needs.

It isn’t recommended that you use this in all of your methods because the technique uses closures and does have a performance penalty. However, if you are having trouble because a certain method is being called too frequently, throttling the processing can result in significant performance improvements.

17 Comments

  1. Jeremy Vinding said:
    July 9, 2007 at 12:10 pm

    while assuring that _process() will be called no later than 250 milliseconds after the last call to process()

    What am I missing… sees that as long as whatever event this keeps happening at an interval

  2. Jeremy Vinding said:
    July 9, 2007 at 2:21 pm

    Wow… that got kinda messed up… I’ll try again.

    The question was supposed to be “It seem to me that as long as the interval at which the event is fired is less than 250 ms, the timeout will be continuously cleared. Thus, the _process will never be called.”

    However, I see now that I mis-read the portion I quoted. It doesn’t assure that “_process” will be called every 250ms like I thought it did.

    if you want it to be called ever 250 ms, no more, and as long as the event keeps firing, no less, then something like this’ll work:


    YAHOO.namespace("example");

    YAHOO.example.MyObject = {

    _timeoutId : 0,
    _process : function () {
    //processing code goes here
    },

    process : function () {
    if( ! this._timeoutId ) {
    var me = this;
    this._timeoutId = setTimeout(function(){
    me._process();
    me._timeoutId = 0;
    }, 250);
    }
    }
    };

  3. On a similiar light, you might want to check out my deferred and sustained events demos as well that i’ve released as part of the Reusable js library i’m authoring.

    Deferred and Sustained Events Demo

    Keep Clicking,

    Bhasker V Kode
    Javascript Hacker

  4. So basically this method is mimicking the onresizing in Firefox. While resizing in IE, _process isn’t called (because the timeout is constantly cleared). Only when the user stops resizing (either by not moving the mouse, or releasing the left mouse button).

    I would actually make use of the fact IE is constantly firing resize-events, and run _process every 250ms (or better: with 250ms intervals; thus minus the time of running _proces).

  5. [...] Throttling your JavaScript [...]

  6. I for one have had first hand experience with IE getting confused because of the repeated onResize call. Thanks for the useful technique.

  7. [...] Downshift Your Code Throttle your code so that a method is called a maximum number of times per second. This can be achieved using timeouts and a little bit of indirection. Throttling the processing can result in significant performance improvements. (tags: Browsers WebDesign) [...]

  8. Hi Nicholas,

    As always you came out with the Good stuff, Thanks. In this case I have a couple of notes:

    - What if I need the event’s reference? in this case the reference can’t be passed to the _process method because the event will expire during the delay.

    - In your code, the _process method will be called 250ms after the firing, and this is Ok for the example (resizing the window), but if you need to process the event immediately and then the process’s method will be unavailable by 250ms, I think this is a useful pattern too.

    The alternate pattern is as follows:


    YAHOO.namespace("example");

    YAHOO.example.MyObject = {

    _timeoutId : 0,
    _available : true,
    _process : function ( e ) {
    //processing code goes here
    },

    process : function ( e ) {
    var me = this;
    if (this._available) {
    this._process( e );
    this._available = false;
    clearTimeout(this._timeoutId);
    this._timeoutId = setTimeout(function(){
    me._available = true;
    }, 250);
    }
    }
    };

  9. @Doekman – this technique can be used to mimic FF’s resize behavior, however, that’s not it’s only purpose. Imagine a behavior that’s attached to a button. If the user clicks it too often, it may spawn too many calls to the same function, slowing the browser down. This technique can effectively be used to throttle processing at any point in your application

    I wouldn’t recommend using setInterval() because it will cause the processing to occur at periodic intervals; this code makes the case that you don’t want it to happen at regular intervals, however, you want to make sure it doesn’t happen too often within a set period of time.

    @Caridy – That is a good question. It’s my position that processing should be separate from event handlers and the event object should never be passed as an argument to function that does processing (please see my talk for more details on that).

    I’d recommend assigning an event handler that, in turn, calls process(), such as:

    function handleResize(e){
    YAHOO.example.MyObject.process();
    }

    If you need information on the event object, I’d recommend just passing in the information that you need rather than the entire event object (again, for maintainability purposes):

    function handleResize(e){
    YAHOO.example.MyObject.process(e.type);
    }

  10. @Nicholas: I was actually trying to say politely: when you constantly call “process”, nothing happens.

    @Caridy: your solution is much better, but I consider it the same pattern, only a better revision. The only thing is you might not handle the last call to “process”.

    My call:


    YAHOO.example.MyObject = {

    _timeoutId : 0,
    _work2do : false,
    _process : function ( e ) {
    //processing code goes here
    },

    process : function ( e ) {
    var me = this;
    var handler = function() {
    me._process( e );
    if (me._work2do) {
    me._work2do = false;
    me._timeoutId = setTimeout(handler, 250);
    }
    }
    this._work2do=true;
    if (!this._timeoutId) {
    handler();
    }
    }
    };

  11. @Nicholas – my intention with the pattern was to show a possible solution for an scenery, and I was trying to delineate the responsibilities of each methods, in the first play I was thinking in this scenery:

    A push button to start an animation (250ms animation), you can click the button during the animation, but this event will not affect the animation process.

    The responsibility of the process methods are:
    1. To offer a public entry to start the process
    2. To determine if the entry is available
    3. To control the availability timer

    The responsibility of the _process method are:
    1. This method is a behavior who get the event, and drive it in to the certain component or apply certain functionalities directly.
    2. This method could be outside of the object (YAHOO.example.MyObject) and using another scope.

    Here is a new scenery:
    If the process method also has a new responsibility: collecting the event information (ex: e.type), and eventually you need another information from the event (ex: mouse position). In this case you need to modify the process method, to solve the problem, and from the point of view of the maintenance and the logic of the business, the _process method is the one who must do this job.

    I’m seeing this in 3 layers:
    1. process method: mechanism to control the availability of the real method
    2. _process method: collect the event information, and begin a certain behavior
    3. external or internal method: who do the real job

    @Doekman – About your code:

    1. In the first pass, you call _process, and set the timeout
    2. (less than 250ms) you set the _work2do=true only
    3. when timeout arrive, (the event object was expired, you can mitigate this issue passing the event’s attributes according to Nicholas), you call _process and set the work2do=false, and set the time out again
    4. when the second time out arrive, you call _process again, with the last event reference (who already expire, twice)
    5. And at this point, you’re back to the first state…

    Ok, I got it, you vary my pattern to warrant the execution of the last event (even when this event are fired between a delay’s process).

  12. @Doekman – You are absolutely correct. If you call process() repeatedly, nothing will happen…until 250ms after the last call, which is what this pattern is all about.

  13. That’s a good tip Nicholas. This advise also applies to listening to events like onTween or onDrag for the animation and drag ‘n drop utilities where it might be best (sometimes) to simply just poll at a slower interval and fire off those events.

  14. @Nicholas: I think, I was sent into the wrong direction by you resize-example…

  15. Great article and very timely. I’ll be using this pattern to throttle resize triggered UI redraws in IE. Thanks!

  16. Nice tip Nicholas =)

    The examples provided by Jeremy Vinding would allow the function to occur the very moment the resize event occurs and every 250ms afterwards as resizing events are sent in Internet Explorer. In contrast, Nicholas’ tip mimics Firefox and the function will be called 250ms after resizing has not occurred for 250ms, which we assume means resizing has stopped.

    Very nice.

  17. [...] happening to prevent the UI thread from clobbering itself.Either way, this article saved the day:YUI: Downshift Your CodeWhile we didn’t follow this article verbatim, it was very helpful when staring this IE [...]