TL;DR: Use a looped animation for waiting times between 2000ms and 10000ms. A progressbar is best to be used for waiting times from 10000ms to 30000ms. When the expected waiting time is more than 30000ms (half a minute) show a progress bar and a time-to-complete estimation, to inform the visitor when to come back.
linkLooped animation indicator
Looped animations are the easiest to implement from a development point of view —no calculation is needed. If the action is expected to take between 2000ms and 10000ms, a looped animation should help the visitor maintain the perception of a responsive system. Dialogue continuity has a ceiling of approximately 10s , assuming that an action is a dialogue between the machine (computer, phone, etc) and the visitor.
Progress indicators (that include a percentage-done) can be used when the expected waiting time is higher than 10000ms with a ceiling of 30000ms. The continuous feedback provides information about the status of the request, which can help extend the visitor’s maximum waiting time . Progress indicators’ will also inform the visitor whether the system has become unresponsive .
linkAdding a time estimate
Visitors will likely want to switch to another task while waiting for more than 30s [1, 3]. Adding an estimate of time-to-complete in longer than 30000ms waiting times will inform the visitor of when to return to the application.
linkNo loading indicator
In situations where loading times are less than 2000ms, a simple state change of the button should communicate the responsiveness of the system. Adding a loading indicator in those cases could do more harm than good, as the visitor may get confused from the flicker of a new element .
linkA note on micro indicators
Adding a loading indicator as a state update of the button could bridge the gap between the no loading indication and the spinner. As the indicator is perceived as a state update of an existing element, it could potentially minimize the probability of the visitor getting confused that they “missed something”.