21

I was trying the code given in angularjs docs (given here: http://jsfiddle.net/zGqB8/) It just implements a time factory and uses $timeout to update the time object after each second.

angular.module('timeApp', [])
.factory('time', function($timeout) {
    var time = {};

    (function tick () {
        time.now = new Date().toString();
        $timeout(tick, 1000);  // how to do it using setInterval() ?
    })();

    return time;
});

How would I do it using setInterval() function instead of $timeout() ? I know that one need to use scope.$apply() to enter the angular execution context but how would that work in a factory function? I mean, in a controller, we have a scope, but we don't have scope in a factory function?

user183123
  • 239
  • 1
  • 2
  • 5

4 Answers4

38

You can use $timeout as an interval.

var myIntervalFunction = function() {
    cancelRefresh = $timeout(function myFunction() {
        // do something
        cancelRefresh = $timeout(myIntervalFunction, 60000);
    },60000);
};

If the view is destroyed, you can destroy it with listening on $destroy:

$scope.$on('$destroy', function(e) {
        $timeout.cancel(cancelRefresh);
});
Michael Cole
  • 15,473
  • 7
  • 79
  • 96
asgoth
  • 35,552
  • 12
  • 89
  • 98
  • I guess you misinterpreted the question. The setInterval() function of JS calls a function repetedly. – user183123 Jan 09 '13 at 16:49
  • 9
    I know. At the moment there is no setInterval alternative in angular. The only way to simulate it is a $timeout function which calls itself recursively. – asgoth Jan 09 '13 at 16:54
  • lets see how long it lasts before i start getting stackoverflow exceptions – Leblanc Meneses May 02 '13 at 00:23
  • 9
    @LeblancMeneses: there is no recursion here, so there won't be any stack overflow. – bmm6o May 08 '13 at 16:23
  • @KevinBeal: 'do something', whatever your function should do – asgoth Jun 29 '13 at 06:42
  • This answer will wait double the time after the first run. See my answer below. The timeout is constructed to run in 1 minute. When 1 minute expires, it calls the action, then creates a timer that executes in 1 minute. When THAT 1 minute expires, it creates a new timer to run in 1 minute. 1 minute later, the "something" happens and the cycle repeats. The first run works, subsequent runs are double the wait time (2 minutes in this example). If you change line 4 (the second $timeout) to be a timeout of 1ms, the problem resolves without introducing a potential stack overflow. – Tyler Forsythe Sep 26 '13 at 17:32
  • 3
    @TylerForsythe you're right, I've reverted a change which incorrectly renamed `myFunction` to `myIntervalFunction`. There is however no issue with a stack overflow as each `$timeout` is not a recursive call but rather defers the execution of the provided function which will be executed with a new call stack. – ljs Oct 08 '13 at 16:11
32

Update

Angular has implemented an $interval feature in version 1.2 - http://docs.angularjs.org/api/ng.$interval


Legacy example below, disregard unless you're using a version older than 1.2.

A setInterval implementation in Angular -

I've created a factory called timeFunctions, which exposes $setInterval and $clearInterval.

Note that any time I've needed to modify scope in a factory I've passed it in. I am unsure if this meets the "Angular way" of doing things, but it works well.

app.factory('timeFunctions', [

  "$timeout",

  function timeFunctions($timeout) {
    var _intervals = {}, _intervalUID = 1;

    return {

      $setInterval: function(operation, interval, $scope) {
        var _internalId = _intervalUID++;

        _intervals[ _internalId ] = $timeout(function intervalOperation(){
            operation( $scope || undefined );
            _intervals[ _internalId ] = $timeout(intervalOperation, interval);
        }, interval);

        return _internalId;
      },

      $clearInterval: function(id) {
        return $timeout.cancel( _intervals[ id ] );
      }
    }
  }
]);

Example Usage:

app.controller('myController', [

  '$scope', 'timeFunctions',

  function myController($scope, timeFunctions) {

    $scope.startFeature = function() {

      // scrollTimeout will store the unique ID for the $setInterval instance
      return $scope.scrollTimeout = timeFunctions.$setInterval(scroll, 5000, $scope);

      // Function called on interval with scope available
      function scroll($scope) {
        console.log('scroll', $scope);
        $scope.currentPage++;

      }
    },

    $scope.stopFeature = function() {
      return timeFunctions.$clearInterval( $scope.scrollTimeout );
    }

  }
]);
BradGreens
  • 1,357
  • 4
  • 15
  • 31
4

Could you call a normal JavaScript method and then within that method wrap the Angular code with an $apply?

Example

timer = setInterval('Repeater()', 50);

var Repeater = function () {
  // Get Angular scope from a known DOM element
  var scope = angular.element(document.getElementById(elem)).scope();
  scope.$apply(function () {
    scope.SomeOtherFunction();
  });
};
Community
  • 1
  • 1
Greg
  • 31,180
  • 18
  • 65
  • 85
2

Latest release candidate (1.2.0 rc3) has interval support. See changelog

Cemo
  • 5,370
  • 10
  • 50
  • 82