Don’t let their small size fool you, Dashboard widgets are surprisingly CPU hungry. Take each widget’s intensely graphical nature and penchant for polling and parsing files from the internet. Add to this Dashboard’s slew of special effect and the fact that your average user keeps 5 or more widgets running at a time, and it’s not hard to arrive at a resource situation that can make the fans on your G5 kick up in anticipation.
But that’s ok, because these processor cycles are only used when Dashboard is activated. As soon as your widgets fade away into their OpenGL-accelerated purgatory, their resource usage drops to zero and your full-sized applications have the undivided attention of your CPU again. Right?
If we’re talking well behaved widgets, then yes, this is true. Fill your Dashboard with as many of the Apple-shipped calculators, flight trackers, and calendars as you please. When you hide it, you will see no impact on your system CPU (RAM is still consumed, but that’s an article for another day).
What most users (and some widget developers) don’t realize, though, is that Dashboard does not guarantee resources consumed by widgets will be freed once it is hidden. It is the responsibility of each individual widget to keep track of its processing and return it to the system when put away. If designers fail to do this, users will notice their computers run more slowly after Dashboard is run, conclude it’s a resource hog, and maybe even take the drastic step of disabling it all together.
If there is one commandment, then, that widget developers must follow, it is Thou shalt not consume CPU when the Dashboard is hidden! So how can you tell if your widget is leaching processor cycles? And if it is, what can you do to fix it? Read on!
Widget Activity
Activity Monitor is a great application. One part ps, one part top, and generous helping of the HIG makes for good eats where process stats are concerned. You can find your copy in /Applications/Utilities. Launch it, set the “Show” drop-down menu to “My Processes” or “All Processes”, and type “dashboard” into the “Filter” field. Then click on the “% CPU” table header until it displays a down arrow.

Now only widgets are displayed, with those that are taking up processor at the top of the list. If you don’t see any items in your list, make sure you’ve opened Dashboard at least once. OS X doesn’t actually execute widgets until the first time they are viewed.
Once Activity Monitor is set up, it’s good practice to leave it running in the background while developing your widget. As you’re coding, steal an occasional glance at the CPU column. If Dashboard has been hidden for more than a few seconds and this column contains any values greater than 0.00, you have a problem. One of your widgets is eating computing power even when not in use!
The good news is the vast majority of CPU consumption issues are easy to fix. Of all the cycle-hungry widgets I’ve diagnosed since 10.4 was released, the great majority have suffered from one of only two fatal flaws: the improper handling of animated .gifs and/or uncleared JavaScript intervals. But to deal with either of these problems, we’re first going to need some hooks into the widget’s life cycle.
The Hook Brings You Back
Apple — ever prescient in matters pertaining to the needs of Dashboard developers — gifted the widget object with two event handlers that fit nicely into our current discussion: onshow and onhide.
As their names suggest, functions assigned to onshow and onhide are triggered when Dashboard is shown or hidden (respectively. I hope you didn’t need me to tell you that!) Normally, these hooks are assigned at the top level of any included JavaScript file:
if (window.widget){
widget.onhide = onhide;
widget.onshow = onshow;
}
function onhide() {
//stop processing...
}
function onshow() {
//restart processing...
}
Now when Dashboard is hidden, the onhide() function is called, putting an end to all unnecessary processing. When the Dashboard is shown again, onshow() gets called to restart everything that needs to be restarted. The if() block around the assignments above is only there to ensure these don’t get called when opened in something that doesn’t understand widgets (like if you were to try to open your widget in a web browser, for example). It’s not strictly needed if your only going to run your widget in Dashboard, but it’s a good habit to get in to. Who knows what Apple will change in 10.5, eh?
Now that we have a place to put our CPU squelching code, let’s take a closer look at some of the causes of processor overruns.
Looking a .gif Horse in the Mouth
Given Dashboard’s relation to Safari and WebCore, it’s no surprise that the most frequent method used to add animation to a web page is also the easiest way to add animation to your widget. I speak, of course, of animated .gifs! And while the .gif format has some technical — and, dare I say, ethical — limitations, animated .gifs, when used properly, can add a lot to a widget.
Now let me explain the italics above. When popping an animated gif onto a web page, nobody stops to think about how much processing power is involved in rendering it. Why should they? After all, it is seldom the case that more than one web page is displayed at a time. And it’s even more rare that the entirety of a page is shown by a browser (unless it’s a very short page or a very large screen). Finally, a browser is not expected to be “always on”. Most users look at a page and then close their browser until they need to look at another one.
Widgets, however, are different. They are always expected be switched on and at the ready. They are always displayed in their entirety. And there are, on average, five-or-more of them open at any given time.
So a web page that includes a .gif that takes 3% of the CPU to display only burdens your processor a little bit — and even then only while the browser is open and the animated .gif is actually on the screen. But if five widgets include the very same .gif, 15% the processor will be tied up from the first time you open Dashboard to the moment you shutdown your computer. Widget makers can not afford to leave animated .gif running in their widgets. An example widget called PrimeDirective underscores this moral.

Download it, run it, and then click on the “Show” button. An array of all-singing all-dancing animated .gifs appears. Leave these .gifs running and exit Dashboard. Now look at your Activity Monitor.

Scary, right? So what’s to be done?
It turns out animated .gifs only use CPU when they are actually displayed on the widget. Thus, if you hide them by setting their display style to “none”, their usage drops to zero. So all you have to do is give each of your animated gifs has an id property, and then for each one add something like:
document.getElementById("myGifId").style.display = "none";
to the onhide() function we set up above. To display the .gifs again when Dashboard is opened, put:
document.getElementById("myGifId").style.display = "inline";
into onshow(). This is more or less what the PrimeDirective widget does when “Auto-disable” is set to “Yes”. Go ahead and show the animated .gifs and click on the “Auto-disable” button until “Yes” is displayed. Now exit Dashboard. See how the CPU percentage drops to 0.00? Taking care of .gifs is easy!
Illicit Intervals
Dealing with JavaScript intervals is less so. In fact, my recommendation to you is not to use them at all! setInterval() and clearInterval() should be as verboten as goto. The potential for abuse is too great.
And alternatives exist! If you need a function to execute after a given delay, setTimeout() and clearTimeout() are better choices. They execute once and only once, so there’s no chance they can continue abusing the CPU on into the night. And if you really need to loop a given function over and over, the AppleClasses now include AppleAnimator and AppleAnimation which let you loop a function and specify a total duration — again eliminating the chance that a function might spin until the cows come home.
But if you have to use an interval, best to learn how to use it safely. Any time you call setInterval() make sure you are assigning it to a global variable so that you can clearInterval() it at a later time. And then make sure you do clearInterval() every interval whenever Dashboard is hidden. The PrimeDirective widget shows what can happen if you don’t. Start it up, make sure that “Auto-disable” is set to “No”, and click the “Start” button. This sets up an interval that displays a random number once every .1 seconds.

If you exit Dashboard now and look at Activity Monitor, you’ll see that once again your widget is eating someone else’s lunch. And that’s only with a single interval left running!
The solution is simple enough. Clear all intervals! For every setInterval() you have in your code, you should have a matching clearInterval() in your onhide() handler. You can then use onshow() to start it back up again. Something like the following:
var timer = null;
var active = false;
function someEvent(event){
timer = setInterval("doSomethingOverAndOver()", 100);
active = true;
}
function onhide(){
if(null != timer){
clearInterval(timer);
timer = null;
}
}
function onshow(){
if(true == active){
timer = setInterval("doSomethingOverAndOver()", 100);
}
}
Again, PrimeDirective implements something very similar to this when “Auto-disable” is set to “Yes”. Check it out!
Am I Missing Something?
As I said, most of the erroneous processor pounding I’ve seen from widgets has been the direct result of one of these two issues. But that doesn’t mean .gifs and intervals are the only things that spin their wheels behind closed doors. I’d love to hear some of your stories about bugs that caused (hopefully beta!) versions of your Dashboard creations to put your CPU in a sleeper hold. Leave your comments below.


Um.. why did your RSS feed shown the entire article? I thought RSS was to show a simple feed?
I thought RSS was to show a simple feed?
Actually, I think you'll find RSS only promises simple syndication. It makes no claims as to the complexity of the syndicated content ;-)
But to answer your question, it's a policy thing. O'Reilly has made the decision that the entirety of its articles should be included in the RSS feed. I think this is a smart move as most RSS readers (Safari included) allow you to define how much of the feed's articles you want presented to you. If you only want to see the first paragraph of each article, dial this value down. That way you're happy without messing things up for the next person who wants to read the entire article in their RSS browser.
But as I've said before, if this imposes some burden on you and you think it should be changed, let me know. I'll take the issue to my web-master overlords for consideration.
Thanks for the insightful article - I hope you decide to follow this up with that future article you mentioned detailing widgets and RAM usage...
...it is seldom the case that more than one web page is displayed at a time. And it's even more rare that the entirety of a page is shown by a browser (unless it's a very short page or a very large screen). Finally, a browser is not expected to be "always on". Most users look at a page and then close their browser until they need to look at another one.
While I see the need to compare .gif format as used in the two different situations, and I find your dashboard solution ituitive, I can't be the only person who finds these particular assumptions utterly untrue. I almost always have multiple tabs open, and I surely don't just close the whole browser when I'm done looking at one page. And while it is rare for a browser to have loads of animated .GIFs visible and active, it's certainly not unheard of.
I almost always have multiple tabs open
Which is why I say, "it is seldom the case that more than one web page is displayed at a time." (bold added). If you have twenty tabs loaded, it is still the case that only one of those tabs can be displayed at a time. In Safari, .gifs only consume CPU when they are actively being displayed — that is, when they are on the currently selected tab.
I surely don't just close the whole browser
I'm not suggesting that people close the whole browser. They don't do a Safari->Quit after every web page, for example. But the average user does either close or minimize the browser window when done looking at a page. The reason is simple. Most people are confused by by having too many open windows on the screen. They habitually close or minimize anything they are not working on to "reduce clutter" (unless it's gmail, which doesn't contain any animated .gifs anyway). As developers, we're used to having kajillions of windows open. And we often have large/multiple screen's worth of real estate to spread things out upon. And so I don't doubt that you might leave your browser windows where the lie. I merely suggest you are not representative.
it's certainly not unheard of
Though I'll bet you betan dollars that you close that window as soon as you're done looking at it. ;-)
Hi. Thanks much for your article. I am not a widget developer but have some web dev experience though more often I work with sound in some capacity. I wondered if it is possible/advisable to rewrite widgets developed by others once they are installed? Of course one of the most useful Widgets I have installed, an audio-oriented calculator, also apparently fails to stop processing... or maybe I should just forward the developer your article. Thanks again.
CDB
I wondered if it is possible/advisable to rewrite widgets developed by others once they are installed[.]
This is a simple question with a simple answer: sometimes. Addressing the question purely from the perspective of possibility, you can modify any uncompiled code you get. This is a benefit for users, but it is seen as a liability by developers who wish to protect their code with more than a license statement (my experience is that those developers, when scripting rather than writing in compiled languages, write some of the worst code out there).
This raises another key question: Do you have the right to modify someone else's work? Strictly speaking, once the code is sitting on your machine, you have the right to do whatever you want to it, so long as the modified code never leaves your custody. Don't go sending a widget to a buddy after you have modified someone else's code. You should comment any changes you make, and if they are noteworthy improvements, contact the widget's creator, if possible, and help out. Most free widgets are GPL or Creative Commons, and you can earn yourself a credit in someone else's code, which, while not top billing, doesn't suck.
See? I told you it was simple.
please is yahoo! widgets save or is it ok?