Web 2.0: A Collection of SetTimeouts

Earlier I blogged about terrible UI responsiveness resulting from a poorly placed setTimeout. I’ve long suspected that SetTimeouts are to blame for everything. Now with help of bz and sfink I have proof.

Turns out webpages like to keep users entertained and spin setTimeout loops to poll the servers to synchronize news tickers, social networking shoe-ins, collaborative editing, etc. Most pages do this regardless of whether they are a background or a foreground tab. Firefox tries to mitigate this by not allowing background tabs to schedule setTimeouts < 2seconds. Turns out this is a pretty weak defense.

In my personal browsing: etherpad, twitter, zimbra burn through cpu cycles. See this bug comment for an example of setTimeout terrorism with less than 10 tabs. In Firefox these setTimeouts cause significant UI lag, but they will also eat your battery life, overheat your laptop, etc. I filed bug 715376 to add functionality to cope with this. The plan is to prioritize foreground tab activity and do exponential setTimeout decay on abusive background tabs. We basically have to write something similar to an OS scheduler. Ideally we’d also follow that up with unloading idle tabs, ie bug 675539.

If you are curious about what tabs are abusing your browser my diagnostic builds will be available on try in a few hours. Install a modified version of about:telemetry to see the report.

What should well-behaved web apps do?

You can detect when your tab becomes inactive via window.onblur, then throttle or disable your page activity. I know using focus is suboptional for this. There is also a vendor-prefixed visibility api (thanks!).

Now you know why your browser keeps your CPU wide awake

Update:

Wrote this post in a rush on the way out, thanks for the visibility links. I am aware that some webpages need to do work in the background. However that work should be as minimal as possible, this is often not the case.

Project Idea:

Would be nice if someone wrote an extension that goes through tabs and nukes any outstanding setTimeouts/XHRs/etc.

This entry was posted on Wednesday, April 18th, 2012 at 4:13 pm and is filed under snappy.
You can follow any comments to this entry through the RSS 2.0 feed.
Both comments and pings are currently closed.

Being in the background doesn’t necessarily mean that a page shouldn’t do any work though. Many sites use the tab title, or even the favicon to indicate updates, both of which are visible from another tab.

That said, I’m sure there is still a lot that could be self-throttled by observing window.onblur, and continuing the work on prioritizing foreground tabs in the browser is definitely a good idea.

Why, oh why, don’t all browsers have a user preference for banning background tabs from running setTimeout and doing other things that suck resources? The web browser is my most hated application for exactly this reason.

We’ve talked about just sending nothing to background pages in Fennec. It was… a debate we should probably pick back up someday. I’m not convinced its a huge source of drain on devices (i.e. with our current behavior we don’t seem to be using to much battery, but there could be lots of reasons for that).

The only reason I had the visibility api in my awesomebar today was because I had looked once to see if calling preventDefault() on the event would prevent pages from being throttled. Would be nice for something like a Pandora (answer, it doesn’t).

I think it would also be very interesting to see what other browsers do in order to mitigate this type of problem. My primary worry is that web content might rely on de facto semantics here which might lead to bad UX if we decide to prevent against this type of thing in Firefox, while other browser vendors don’t…

(Not to say that we should not react on this, we definitely should! But maybe after talking to other browser vendors?)

Also, this looks like *great* material for a hacks blog post advertizing the better ways for doing this kind of stuff to web developers.

Yessssssssss I hope this gets fixed very soon. I like to load up Firefox on my netbook with one or two hundred unread tabs when I have a net connection and then read them when I don’t, and it halves my battery life because Firefox barely ever drops under 30% CPU with constant 60–70% spikes. I will be so grateful when this is dealt with.

If we want to promote the page visibility API, we shouldn’t self-sabotage the effort by making it annoying to use by giving it different identifiers in each browser. Let’s get rid of the vendor prefix!

@tglek: Killing XHRs and other requests shouldn’t be hard – they are all attached to the load group of the document so doing nsILoadGroup.cancel() should do. The problem is that the web page will get notified and might start another request immediately (yes, some websites are even crazy enough to go into an endless loop if requests are blocked). Temporarily switching off nsIDocShell.allowJavascript might work around this problem – but it will also leave the web page in an inconsistent state. Maybe nsILoadGroup.suspend()/nsILoadGroup.resume() will work better (not sure whether these methods are even implemented however).

Timeouts are more complicated, I don’t think that an extension can check which ones are active. Timeout identifiers are assigned sequentially however, so calling setTimeout() on the window will do to give you the current maximal identifier. One can then go through all identifiers from 1 to max and call clearTimeout() on them – might take a while if the web page creates lots of timeouts however. Still, some kind of pause/resume would be better, otherwise the web page will be left in an inconsistent state.

Nuking all activity from background tabs seems wrong. Because then I can’t cue up a new page in a new tab and continue reading another while it gets ready.

I believe, a setTimeout is also to blame for the delay of the display of the suggestion list when pressing a key in the address bar (in Chrome the suggestion list appears at exactly the same time as the pressed letter appears in the address bar textbox).

Just found nsIDOMWindowUtils.suspendTimeouts()/resumeTimeouts() – that should take care of the timeouts. Then again, nsIDOMWindowUtils.enterModalState()/nsIDOMWindowUtils.leaveModalState() looks even more promising, that should theoretically suspend all script activity in the tab with a single call.

Pausing timeouts/intervals/events totally breaks test automation on UI level. Or at least makes it very prone to stupid errors. So first developer implements feature that breaks test automation, and than brags about how UI level testing is brittle/fragile/non-reliable/hard to maintain…

Anyway, it is not timeout/interval that is causing issues, but bad design, misuse and overuse of the tool, that is root cause here. But that was problem of JS from the beginning.

I’m developing a fitness app that uses a lot of setTimeouts (has multiple stop watches). CPU and Battery consumption were always a concern, particularly since the clocks can be running for quite a bit of time (upwards 40:00 at times).

I like your solution for detecting foreground tabs. I may even resort to less frequent polling

http://fitzgeraldnick.com/weblog/40/ is a great post about “timer congestion,” and his answer is a spiffy library “chronos” that does two things, first creates a single timer loop, and second, slows the loop under load. i’ve long wondered whether a single timer loop might be less intensive on a page than multiple timers all going off.

but for sure, not doing any activity at all is going to be a lot better than this kind of micro-optimization. a+, good post, important topic.

@Wes, I suspect that’s more due to the fact that we still wake up fennec too often. It only takes a few wakeups to ruin a cpu sleep. Are there specific power numbers somewhere?

@Wlad, this would be so cool if you or someone else could make it work. I don’t have enough cycles for this. Such an extension would be handy for power users AND it would be useful gauge potential benefits from any improvements we do.

@avih, it’s possible if very hard. Problem is that the web allows cross-frame communication which means they can share objects, which means they have to share a thread..so the problem is still present there.

in fact the browser should not only disable the background js, but also stop any plugin and animated gifs (e.g. adbanners).

even better, in the best case it would unrender the page, free all buffers except of the internal state as far as possible and restore the page from disk-mmaped HTML when the tab is switched instead wasting hundreds of MB of RAM.

but then, firefox is full of memory leaks so only a restart every 20 or so used tabs is necessary to keep mem consumption below 1 Gig…

I cannot help but comment half way through reading this post with the dead obvious: YOU ASKED FOR IT!

Every freaking day evangelists like Heilman talk complete crap about how flipping amazing the web is. Many others go on about how we wont need OSes anymore and talk about webapps endlessly … well mate, you asked us to do this, you endless pimp the open web and think that some amazing application (a browser) running inside an operating system, can magically handle endless complex applications as well as any desktop OS can.

Well, quite frankly, you’re all talking utter rubbish and I’m sick to death of it. Incredibly belated honesty from the excellent MemShrink guru (no surprise it took just one of us Aussies to tell it like it is when hundreds of others never did!) and now this “ooh web2 is too much for Firefox to handle” just confirms it: the “open” in web might as well refer to open-ended scalability which is just completely FALSE!

It’s time the web browser industry started talking about limitations! Flipping JavaScript engines may have gotten a lot faster (though the pace has now died it seems?) but have they scaled in memory and performance terms? NO! Background worker threads clearly don’t seem to be making a big enough difference. Or maybe it’s not JS at all and merely the atrocious pace of Mozilla in converting the code base to multi-threading in general? What happened to Electrolysis? Why was that killed?

Arrrgh… as a web developer and user, this is super frustrating. Mozilla needs to start doing the right thing by stopping their endless pimping of the web as a solution for everything but the kitchen sink.

From now on, every time I read a Mozilla developer complain about writing browser code that struggles cope with real-world usage, I’m just going to hit up the same question: why aren’t you educating developers and end users about the limitations of the web? You can’t expect developers and users to write sites and use your browser in a browser-friendly manner if you do not explain what that is? I’ve already said this on hacks.mozilla. All they ever pimp on that site is how to make some freaky useless bleeding-edge-case code work and how the web is everything to everyone endlessly scaling without any dramas. This must stop. It must be countered by reality. Stop employing HTML5 evangelists and start employing web.now realists!

@pd, it takes an aussie to fix software right and a kiwi to complain politely

I actually agree that in an ideal world developer outreach should present a more nuanced picture that talks about downsides. I like to think that talking about gotchas is a common theme in my blog posts.

Personally, I hate working on features. I spend all my time fixing them to work better. I love that my job lets me do that.

Apologies for my lack of politeness. Admittedly I struggled to contain my frustration and I was in a hurry. I’ve made lots of posts of Mozilla-related blogs lately that are less aggro and more constructive, I just failed to contain myself enough this time. We all make mistakes I guess.

I think it’s also worthwhile pointing out that the care factor or passion that generates the mood I was in a good thing I reckon. If I didn’t care about the future of Firefox I’d not be as vocal. I’d probably just be one of the Chrome users who tolerates the lack of customization in that browser but enjoys a lot of it’s other features.

Thanks for your considered reply. I don’t write what I did to generate flame wars or to act like a troll, I guess I just see a lot of contradictions in the Mozilla world and it worries me that this apparent sign of organizational disconnection will cause Firefox to fail.

I actually like lazy loading tabs in the background.. so I can do something in the foreground tab while the background tab loads.. especially in an environment where a webpage will load the data when its ready from the server, it polls the server for an updated page and loads that when its finally ready.