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.

34 comments

  1. 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.

  2. 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.

  3. 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).

  4. 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.

  5. 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.

  6. Greg, I feel the same way. It’s nice when webpages do stuff for me, but it’d be nice to be able to tell them to bugger off. Perhaps someone can do up an extension for that.

  7. 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!

  8. @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.

  9. 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).

  10. 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.

  11. Can background tabs get moved to their own threads?

  12. 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.

  13. Could you borrow code from the bfcache and implement a “tabcache”? So switching away from a tab would be like navigating away from it. Stops all activity, but is still quick to restore.

  14. Great post

    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

    Thanks!

  15. This is a sensational to the point post. Being a responsible developer means that we should be implementing such solutions as you have mentioned by pausing our processes onblur.

    jQuery should absolutely include this in their animate function.

  16. 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.

  17. This probably explains why the new BBC Sport website commentary pages don’t update unless the tab is active. Good stuff.

  18. It would be nice if setTimeout() actually worked in firefox..

  19. IEBlog has a good article on slowing down timeouts: http://blogs.msdn.com/b/ie/archive/2011/07/08/using-pc-hardware-more-efficiently-in-html5-new-web-performance-apis-part-2.aspx

    @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.

  20. @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.

    @Dan, that’s an amusing idea. No idea how feasible it is.

    I really appreciate everybody’s feedback on this.

  21. If/When background tabs are throttled (even more), the user definitely should have an option to *pin* them for no throttling.

    A use case: Taking a website backup, which uses AJAX. While status-polling may not be necessary, incrementally passing tasks is.

    For any background task doing or passing some logic rather than only updating UI and polling new data, throttling is a bad idea.

  22. requestAnimationFrame is what I’ve seen toted as being what to use.

  23. 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…

  24. […] little investigation into setTimeout overhead exposed more overhead than I expected. After our regularly scheduled snappy meeting, we had a […]

  25. 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!

  26. @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.

  27. Hi

    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.

    Long live the ‘fox! Lurv that fluffy tail :)

  28. @molkex:
    Many ideas you are talking about are already filled in Mozilla’s bug tracker, often blocking https://bugzil.la/595574 (Reduce performance hit of loading background tabs), for example:
    * Stop animating images in background tabs: https://bugzil.la/588975
    * don’t automatically start plugins in background tabs: https://bugzil.la/495840

    @pd:
    You have similar problems in the traditional development world too, but since you are apparently just a “web developer” you might not know about this.

  29. Andrew Clayton

    @pd:
    “why aren’t you educating developers and end users about the limitations of the web?”

    Being a web _developer_, I’d have thought _you’d_ know what its limitations are.

  30. This is good because finding these little leaks will lead to a better performing product. This one seems to be a major problem.

  31. 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.