1

I am loading a lot of data and it seems to take a few seconds to paint the page even after the data has been received.

I'm simplifying my code below so please excuse any syntax errors in this toy example. My angularjs code is structured more or less like this

$scope.isLoading = true;
$http.get(url)
  .then(function(resp) {
    $scope.mydata = resp.data;
  })
  .finally(function() {
    $scope.isLoading = false;
  })

And my html is

<div class="spinner" ng-show="isLoading">
...
<!-- lots of data and complex DOM manipulation below -->

I also tried

<div class="spinner" ng-hide="mydata">
...
<!-- lots of data and complex DOM manipulation below -->

In both cases, I get a spinner, then the spinner disappears, then several seconds go by, before the data finally shows up. If the data is smaller, then the delay is much less noticeable.

When I look at my browser's developer tools, the data is received and the spinner immediately goes away. Then the multi-second delay happens before the page finally loads. So I suspect the browser is trying to write the DOM

I would like to trim the delay between the spinner disappearing and the page appearing.

Is there some sort of hook/callback that's invoked after all the DOM is written?

kane
  • 5,465
  • 6
  • 44
  • 72
  • You could try `$scope.$applyAsync(() => { $scope.isLoading = false })` – Phil Nov 16 '17 at 23:18
  • 1
    If you are rendering with `ng-repeat`, consider the answers in: [ng-repeat finish event](https://stackoverflow.com/questions/13471129/ng-repeat-finish-event/). – georgeawg Nov 16 '17 at 23:28
  • @Phil $applyAsync() did not help, but thank you for introducing me to this function – kane Nov 17 '17 at 18:15
  • @georgeawg unfortunately, the structure is much more complicated than a single ng-repeat but I will look to optimize – kane Nov 17 '17 at 18:15

1 Answers1

1

Since visual delay is caused by the fact that DOM is updated synchronosly on digest synchronously and blocks main thread, spinner removal should be postponed at least for one tick:

$http.get(url)
  .then(function(resp) {
    $scope.mydata = resp.data;
  })
  .finally(function() {
    return $timeout(function () {
      $scope.isLoading = false;
    })
  })

Timeout delay can be increased to non-zero value (e.g. 20), depending on how the parts of the application that cause a freeze work. A new digest caused by $timeout can cause a new freeze, so the problem possibly should be solved with optimizations.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • I understand the solution but it's not ideal. I don't really know what timeout to set. Sometimes, the data is small and it loads fast and other times it's long. Not sure if I can calculate the DOM load time based on data size either since that partly depends on browser's processing power – kane Nov 17 '17 at 17:58
  • You need to set 0 timeout (the alternative to it is $scope.$applyAsync, as was mentioned in comments), unless proven otherwise. This depends on directives that cause the app to freeze, not data size or freeze duration. – Estus Flask Nov 17 '17 at 18:39
  • I tried both $timeout and $applyAsync. The spinner disappears right away with 0 timeout and I still have the page delay on very large data sets – kane Nov 17 '17 at 19:14
  • Did you try something like 10 or 20? Because again, this depends on directives. That's why I mentioned that 0 won't necessarily work. – Estus Flask Nov 17 '17 at 19:20
  • Interesting, setting it to 0 or 1 doesn't work but setting it to 10 or 20 as you suggest does. I'm not sure I understand this? Could you please clarify what's happening with your solution either in the comments or edited solution? Thanks – kane Nov 17 '17 at 19:28
  • If a directive involves a delay, $timeout will require a bigger delay than a directive has, in order to stop a spinner after DOM update. E.g. if I remember correctly, ng-repeat introduces 0 delay itself when updating DOM, i.e. it won't be compiled synchronously. There may be other directives in your app that introduce delays but ng-repeat is enough to explain this, I guess. Small delays may affect nothing, so 10 is a reasonable choice https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Timeouts_throttled_to_%3E4ms – Estus Flask Nov 17 '17 at 19:35