Did you use a window.setTimeout in an Angular app to update any element in the web page and it didn’t work out? If yes, then the stack overflow posts must have guided you to put a $.apply() in the setTimeout body and make it work. If you have been through such scenarios then this post might be for you. The intent here is to chalk out the differences between $timeout and setTimeout, pros and cons of both the functions and compare their performance.

Let’s start with window.setTimeout. window.setTimeout accepts the function to be executed as the first argument and the time to delay that function’s execution by (at least) given milliseconds. The setTimeout function returns a timeoutID which is a non-zero numeric which identifies the timer created by calling setTimeout and this value can be passed to clearTimeout to cancel the timeout.

var timeoutID; function delayedAlert() {

timeoutID = window.setTimeout(slowAlert, 2000);

} function slowAlert() {

alert(‘That was really slow!’);

} function clearAlert() {

window.clearTimeout(timeoutID);

}

This is an example picked from MDN documentation which shows the use of window.setTimeout in order to display an alert message after a timeout of at least 2000 milliseconds.

Angular $timeout is a wrapper written for window.setTimeout in form of a try catch block which throws exceptions via $exceptionHandler service. $timeout accepts the function to be delayed, delay time, a boolean to invoke $.apply and parameters to be passed to the function.

This is how $timeout can be used

$timeout([fn], [delay], [invokeApply], [Pass]);

fn — the function to be executed after the timeout

delay — the delay or timeout after which the fn should get called

invokeApply — a boolean to invoke $.apply() — true by default.

Pass — additional parameters to the function

$timeout( function(){ $scope.test1 = “Hello World!”; }, 5000 ); //time $scope.time = 0; //timer callback var timer = function() { if( $scope.time < 5000 ) { $scope.time += 1000; $timeout(timer, 1000); } } //run!! $timeout(timer, 1000);

As we are clear with the specifications of both $timeout and setTimeout, let’s move on to their specific behaviour now.

Angular provides a sweet feature of two way data binding which means that the data will be updated when corresponding view is updated and vise-a-versa, the view will be updated if the corresponding data is updated. Angular achieves this by digest cycle. (Will soon share a detailed post for digest cycle)

Every time there is an update within the Angular scope, $digest is called and updates the views/model. Angular doesn’t directly call $digest(). Instead, it calls $scope.$apply(), which in turn calls $rootScope.$digest(). As a result of this, a digest cycle starts at the $rootScope, and subsequently visits all the children scopes calling the watchers along the way. So, whenever a function is called inside a $timeout, $timeout invokes $.apply() on basis of the invokeApply boolean (an argument given to $timeout — read spec here) which is true by default. Any function which is not part of model will not call $.apply(). In that case, $.apply() has to be invoked manually from that function in order to update model/views. Hence, in order for setTimeout to work and update the views, $.apply() has to be called from within the function supplied to setTimeout.

An example using $timeout

$scope.timeInMs = 0; var countUp = function() { $scope.timeInMs+= 500; $timeout(countUp, 500); } $timeout(countUp, 500);

An example using setTimeout and apply.

$scope.timeInMs_old = 0; var countUp_old = function() { $scope.timeInMs_old+= 500; setTimeout(function () { $scope.$apply(countUp_old); }, 500); } setTimeout(function () { $scope.$apply(countUp_old); }, 500);

Although, if you don’t need to update any views/model using setTimeout which would not need digest cycle to run, you can use setTimeout as it is without invoking $.apply. Albeit, doing this might introduce inconsistencies when consequent changes are made to model or view.

Here is a gist with the all the examples consolidated in one file.

If you like this post, please share it and share the love by clicking on the small clap there.

Also, please feel free to comment and share your thoughts on this.