1

I have a function in a service that takes a long while to calculate so I would like to show a message on the scope to show it's calculating. I have created a (simplified) jsfiddle here: http://jsfiddle.net/rikboeykens/brwfw3g9/

var app = angular.module('myApp', []);

app.service('testService', function(){
    this.longFunction=function(){
        for (var i=0; i<1000000000;i++){


        }
    }
});

function TestCtrl($scope, testService)
{
    $scope.$apply(function(){$scope.waiting="not waiting";});
    $scope.longFunction = function(){
        $scope.waiting="waiting";
        testService.longFunction();
        $scope.waiting="not waiting";
    }
}

The longFunction on the testService takes about two seconds to complete, but $scope.waiting is not updated to "waiting" while this is taking place.

I tried using $scope.$apply but then I just get an error saying $apply is already in progress.

Would it work if I perform the longFunction on the testService asynchronously? I have been looking into using promises but I'm not sure how I would go about implementing them.

  • Since your long function is synchronous, there's no way for angular (or any other javascript mechanism) to update the scope. If you want to display messages during a costly calculation, async is indeed the way. – doldt May 27 '15 at 07:06
  • Every answer up to now gets the point. Just as an added value, you may want to take a look at [this answer](http://stackoverflow.com/questions/23003130/angularjs-show-loading-animation-for-slow-script-e-g-while-filtering/23088231#23088231) for a way to keep the UI responsive while executing a long-running service. Additionally you may want to look at web workers (probably a much better solution, but I have no experience in it). – Nikos Paraskevopoulos May 27 '15 at 08:01

3 Answers3

2

You can make use of $timeout.

function TestCtrl($scope, testService, $timeout)
{
   $scope.waiting="not waiting";
    $scope.longFunction = function(){
        $scope.waiting="waiting";
        $timeout(function(){
          testService.longFunction();
          $scope.waiting="not waiting";
        })
    }
}
Zee
  • 8,420
  • 5
  • 36
  • 58
1

It's working, but you don't see waiting message because longFunction runs synchronously and I suspect that its execution prevents the UI from being painted to show waiting message update. When the longFunction call ends, the UI thread isn't busy and it sets the "not waiting" message preventing you from being able to read the "waiting" text.

This is an expected behavior in Web browsers since everything is executed in the UI thread, and no Web browser can prioritize work if operations are synchronous and the UI gets freezed for a while. Since a long for loop or a long calculation can take a lot of CPU resources, Web browsers need to wait until the operation ends to continue painting the screen.

You can use setTimeout flavor in Angular to simulate an asynchronous operation, and this will give priority to UI and you'll be able to see the message change:

$scope.longFunction = function(){
        $scope.waiting="waiting"; 

        // Actually this is a workaround, because JS doesn't provide
        // an elegant way of enqueueing asynchronous operations in the UI
        // layer. BTW, does Web Workers mean something for you? ;)
        $timeout(function() {
            testService.longFunction();
        }, 0);

        $scope.waiting="not waiting";
}
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
1

Definitely you should you $timeout to execute service function. I created and example on Codeopen.io: http://codepen.io/skymk/pen/EjNpKL?editors=101

app.controller('Controller', ['$scope', 'testService', '$timeout',  function($scope, testService, $timeout) {
    $scope.waiting="not waiting";
    $scope.longFunction = function() {
        $scope.waiting="waiting";
        $timeout(function() {
          testService.longFunction();
          $scope.waiting="not waiting";
        }, 0);    
    }
    $scope.longFunction()
}])
Valery Bugakov
  • 355
  • 1
  • 10