Indicate that processor-heavy JS function is running (GIF spinners don't animate)
Asked Answered
T

2

17

Showing then hiding animated indicator / spinner gifs are a good way to show a user that their action has worked and that something is happening while they wait for their action to complete - for example, if the action requires loading some data from a server(s) via AJAX.

My problem is, if the cause of the slowdown is a processor-intensive function, the gif freezes.

In most browsers, the GIF stops animating while the processor-hungry function executes. To a user, this looks like something has crashed or malfunctioned, when actually it's working.

JSBIN example

Note: the "This is slow" button will tie up the processor for a while - around 10 seconds for me, will vary depending on PC specs. You can change how much it does this with the "data-reps" attr in the HTML.

enter image description here

  • Expectation: On click, the animation runs. When the process is finished, the text changes (we'd normally hide the indicator too but the example is clearer if we leave it spinning).
  • Actual result: The animation starts running, then freezes until the process finishes. This gives the impression that something is broken (until it suddenly unexpectedly completes).

Is there any way to indicate that a process is running that doesn't freeze if JS is keeping the processor busy? If there's no way to have something animated, I'll resort to displaying then hiding a static text message saying Loading... or something similar, but something animated looks much more active.


If anyone is wondering why I'm using code that is processor-intensive rather than just avoiding the problem by optimising: It's a lot of necessarily complex rendering. The code is pretty efficient, but what it does is complex, so it's always going to be demanding on the processor. It only takes a few seconds, but that's long enough to frustrate a user, and there's plenty of research going back a long time to show that indicators are good for UX.


A second related problem with gif spinners for processor-heavy functions is that the spinner doesn't actually show until all the code in one synchronous set has run - meaning that it normally won't show the spinner until it's time to hide the spinner.

  • JSBIN example.
  • One easy fix I've found here (used in the other example above) is to wrap everything after showing the indicator in setTimeout( function(){ ... },50); with a very short interval, to make it asynchronous. This works (see first example above), but it's not very clean - I'm sure there's a better approach.

I'm sure there must be some standard approach to indicators for processor-intensive loading that I'm unaware of - or maybe it's normal to just use Loading... text with setTimeout? My searches have turned up nothing. I've read 6 or 7 questions about similar-sounding problems but they all turn out to be unrelated.


Edit Some great suggestions in the comments, here are a few more specifics of my exact issue:

  • The complex process involves processing big JSON data files (as in, JS data manipulation operations in memory after loading the files), and rendering SVG (through Raphael.js) visualisations including a complex, detailed zoomable world map, based on the results of the data processing from the JSON. So, some of it requires DOM manipulation, some doesn't.
  • I unfortunately do need to support IE8 BUT if necessary I can give IE8 / IE9 users a minimal fallback like Loading... text and give everyone else something modern.
Tripoli answered 21/11, 2013 at 17:52 Comment(9)
Have you tried CSS animations?Maurer
Have you thought about moving your computationally-expensive code to a Web Worker?Etana
@ssorallen Good idea - I never normally even think about CSS3 animation since I'm required to support IE8+ (boooo), but if it works, IE<10 users can have Loading... text and everyone else can get CSS3.Tripoli
@Etana - interesting, not come across those before, am googling it now... Paragraph 2 of this writeup from John Resig sounds like exactly what I'm looking for.Tripoli
@user568458 it's kind-of like using a separate thread. You have to pass messages to/from web worker code, so for some applications it's hard to use. The mechanism has to keep the environments isolated because of the lack of synchronization primitives in the language.Etana
Unfortunately Web Workers are only supported in IE10+: caniuse.com/#feat=webworkersMaurer
I came across a nice article where css3 animations keep going on certain browsers allthough there are pretty intense js is performing: phpied.com/css-animations-off-the-ui-thread I'm not quite sure if this may be a feasable solution 4 you but i've tested the html with android stock browser and the red and green cubes kept performing their transitions when pressing the "kill" button.Innuendo
I felt so free to enhance your fiddle. I've tested it with FF but everything freezes. But on Chrome the red and green cubes kept performing smile jsbin.com/iCEsoqID/5/editInnuendo
Ha ha ha, revisiting this question three years later, I was so wrong when I said "The code is pretty efficient"! Also, Chrome Developer Tools' visual memory profiler under Timeline > tick Memory is incredible.Tripoli
D
7

Modern browsers now run CSS animations independently of the UI thread if the animation is implemented using a transform, rather than by changing properties. An article on this can be found at http://www.phpied.com/css-animations-off-the-ui-thread/.

For example, some of the CSS spinners at http://projects.lukehaas.me/css-loaders/ are implemented with transforms and will not freeze when the UI thread is busy (e.g., the last spinner on that page).

Depicture answered 21/11, 2015 at 1:24 Comment(4)
Keep in mind that it's not that simple to keep the animation off the UI thread. If the animated element has any sort of relative unit (e.g. percentage), animation will freeze anyway.Martz
Interesting, in Chrome, #2, #3 and #8 keep spinning on that Luke Haas page if I foul up the processor with var start = +new Date; while (+new Date - start < 2000){}; in IE10 and IE11, #6 only keeps spinning, and in Firefox, it's #2 and #6 that keep spinning... meanwhile all browsers behave the same with the simple squaresTripoli
...a little more testing later, it seems like all modern (IE10+ and ~2014+ versions of other browers, more or less) browsers will allow animations that are only transforms, and browsers will also allow certain other things, but they're so inconsistent in what they allow that trying to keep track of it all spells madness. In short, use CSS transform animations.Tripoli
One final note (!) - on some browsers (notably all versions of IE) even spinners based wholly on transform will freeze if the CPU is doing a high volume of paint / render layer events, which can cause UX problems e.g. process loads file by AJAX, spinner spins; it processes data, spinner still spins if it's only based on transforms; it starts rendering a complex new layout, spinner stops spinning as if it's crashed.Tripoli
W
2

I've had similar problems in the past. Ultimately they've been fixed by optimizing or doing work in smaller chucks responding to user actions. In your case different zoom levels would trigger different rendering algorithms. You would only process what the user can see (plus maybe a buffer margin).

I believe the only simple workaround for you that would be cross-browser is to use setTimeout to give the ui thread a chance to run. Batch up your work into sets of operations and chain them together using several setTimeout calls. This will slow down the total processing time, but the user will at least be given feedback. Obviously this suggestion requires that your processing can be easily sectioned off. If that is the case you could also consider adding a progress bar for improved UX.

Wrongly answered 19/12, 2013 at 20:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.