Mori no Ando
Mori no Ando

Reputation: 12

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.

Upvotes: 1

Views: 515

Answers (1)

quirimmo
quirimmo

Reputation: 9988

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);
}

Upvotes: 2

Related Questions