Last Updated: June 26, 2023
·
378.6K
· otupman

AngularJS: use $timeout, not setTimeout

There are some cases where one needs to perform some sort of timeout operation and we frequently achieve this using JavaScript's setTimeout() function.

However, if we use setTimeout() in an AngularJS application we also need to use $scope.$apply() to ensure that any changes to the scope will be reflected elsewhere (i.e. data-bound in a view).

AngularJS provides a handy wrapper for this: $timeout() - it does the $apply() call for us so we don't have to. Magic!

function Ctrl($scope, $timeout) {
    $scope.timeInMs = 0;

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

    $timeout(countUp, 500);
}

And here's a JsFiddle showing it working: http://jsfiddle.net/otupman/fq4vg/

Update and warning- if you're using $timeout to create what is essentially an interval; don't - it will break your e2e tests :( The workaround is to set the standard setTimeout for the moment :( :( :(

More info on the Angular github issue here: https://github.com/angular/angular.js/issues/2402

12 Responses
Add your response

$timeout is really handy indeed! I'd also like to see $interval alongside $timeout.

over 1 year ago ·

Agreed! If I get the chance I might look into that, though I do think someone else must have written one.... maybe :D

over 1 year ago ·

@otupman Yeah, all kinds of hackish workarounds can be spotted out there in the wild: http://stackoverflow.com/a/14238039/1809349

over 1 year ago ·

Oh wow, I just posted a similar tip after suffering thru a setTimeout() issue for a while on Friday. Wish I had seen this tip on Thursday :D

over 1 year ago ·

Yeah there are a couple of $interval implementations kicking around, with various degrees of success.

Hoepfully it'll be standard soon.

over 1 year ago ·

Great tip!

over 1 year ago ·

As of 1.2 they will have $interval :)

over 1 year ago ·

Just out of interest, can you use $digest straight after changing a scope value instead of wrapping it all in an $apply function? Or does that not work?

over 1 year ago ·

You can use $digest if the $scope data being watched is on the current $scope or child $scopes. A $digest will trigger all the $$watchers down the tree, but won't propagate to sibling or parent $scopes. Use $apply if you want all $$watchers to trigger on the $scope tree.

Also, as noted above, $interval and $timeout trigger $apply, and there are definitely times where you'll want to use a timeout or interval function that isn't changing $scope data. Don't bog down your application by triggering unnecessary $$watches, - know when to use $timeout/$interval and setTimeout/setInterval.

over 1 year ago ·

Thank you! :)

over 1 year ago ·

$interval is now available for those that want to use setInterval: https://docs.angularjs.org/api/ng/service/$interval

over 1 year ago ·

"However, if we use setTimeout() in an AngularJS application we also need to use $scope.$apply() to ensure that any changes to the scope will be reflected elsewhere (i.e. data-bound in a view)."

Awesome, thank you buddy! Helped me a lot!

over 1 year ago ·