Mori no Ando Mori no Ando - 2 years ago 79
AngularJS Question

Dynamically change Angular's $interval delay

I have a tour service that returns a promise which resolves to a tour object. The tour object has some scenes, and each scene has some hotspots. In the main view's controller, I have a "private"

_init()
function that waits for the tour to resolve and then sets up some stuff.

I am using an interval that is supposed to shuffle through the scenes one by one ad infinitum, but I want a delay between the scenes to be based on the number of hotspots of the currently active scene, e.g. a scene with 2 hotspots should last for 7.5 seconds, a scene with 1 hotspot should last for 5 seconds, etc.

Here's what I have so far:

var i = 0, ready, scenesCount, activeScene, duration, transition = null;
_init();

// Later on down...

function _init()
{
ready = false;

AlexaTourApi.getTour( null).then( function ( res) {
$log.log( 'AlexaTourController got tour', res);
$scope.tour = res;
scenesCount = $scope.tour.scenes.length;
activeScene = 0;

duration = (getActiveScene().hotspots.length + 1) * 2500;

ready = true;
transition = $timeout( _doTransition(), duration);
});
}

function _doTransition()
{
activeScene = (i++ % scenesCount);

duration = (getActiveScene().hotspots.length + 1) * 2500;

transition = $timeout( _doTransition(), duration);
}


EDIT:

I implemented some new code as per quirimmo's suggestion:

function _init()
{
ready = false;

AlexaTourApi.getTour( null).then( function ( res) {
$log.log( 'AlexaTourController got scenes', res);
$scope.tour = res;
scenesCount = $scope.tour.scenes.length;
activeScene = 0;

duration = (getActiveScene().hotspots.length + 1) * 2500;

ready = true;
_resetInterval();
});
}

function _resetInterval()
{
if ( transition !== null) {
$interval.cancel( transition);
}

duration = (getActiveScene().hotspots.length + 1) * 2500;
transition = $interval( _doTransition(), duration);
}

function _doTransition()
{
activeScene = (i++ % scenesCount);
}


Only now I'm getting an
f is not a function
exception.

Answer Source

You have a $timeout which calls recursively another $timeout inside. And so on. This means that the timeouts will never stop, because they stop when the code executed inside will stop.

Change your code using $interval and before to start the new one, clear the previous one. And it should work fine.

P.s. equivalently I think you can also cancel the prev timeout before to start the new one. See $timeout.cancel method

var i = 0,
  ready, scenesCount, activeScene, duration, transition = null;
_init();

// Later on down...

function _init() {
  ready = false;

  AlexaTourApi.getTour(null).then(function(res) {
    $log.log('AlexaTourController got tour', res);
    $scope.tour = res;
    scenesCount = $scope.tour.scenes.length;
    activeScene = 0;

    duration = (getActiveScene().hotspots.length + 1) * 2500;
    ready = true;
    clearTimeoutAndStartNewOne();
  });
}
// here we clear the timeout and then we start a new one which is connected to the other function
function clearTimeoutAndStartNewOne() {
  if (transition !== null) {
    $timeout.cancel(transition);
  }
  duration = (getActiveScene().hotspots.length + 1) * 2500;
  transition = $timeout(_doTransition, duration);
}
// here we simply implement the logic of the timeout to be repeated
function _doTransition() {
  activeScene = (i++ % scenesCount);
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download