Sildoreth
Sildoreth

Reputation: 1915

Can setTimeout be used with a function inside a closure?

Can JavaScript's setTimeout function be used with functions that are inside a closure? What if the call to setTimeout is made from within that same closure?

With my attempts, it appears that you cannot. I'm not sure if I'm missing anything, though.

Basically, I would like to do this:

var ButtonCycler = (function() {
  var ButtonCycler = {};
  var autoCycle;
  var idx = 0;
  var buttonIds = [];
  buttonIds[0] = "id1";
  buttonIds[1] = "id2";
  //"Public" methods
  ButtonCycler.startAutoCycle = function() {
    autoCycle = true;
    cycleButtons();
  }
  ButtonCycler.stopAutoCycle = function() {
    autoCycle = false;
  }
  //Worker function (effectively private because it's in the closure)
  function cycleButtons() {
    var radioButton;
    if(autoCycle) {
      radioButton = document.getElementById(buttonIds[idx]);
      radioButton.checked = true;
      idx++;
      if(idx >= buttonIds.length) {
        idx = 0;  //Start over at first button
      }
      setTimeout('cycleButtons()',2500); //Delay for 2.5 seconds
    }
  }
  return ButtonCycler;
})(); //IIFE

Then in onload for the page, I can do this:

ButtonCycler.startAutoCycle();

And when the user manually clicks one of the radio buttons, I can do this:

ButtonCycler.stopAutoCycle();

And the mechanism for performing the cycling can be nicely encapsulated in the closure.

When I try this, I get an error that cycleButtons does not exist. The initial call to cycleButtons succeeds, though. It is the call initiated by the call to setTimeout that is failing.

Upvotes: 0

Views: 66

Answers (2)

KJ Price
KJ Price

Reputation: 5964

You are passing cycleButtons() as a string which will later be called using eval. This will assume that cycleButtons is available at the global scope (which it is not). Instead, pass the reference to the actual function:

setTimeout(cycleButtons, 2500);

Upvotes: 5

Hoyen
Hoyen

Reputation: 2519

Your setTimeout is incorrect and you should probably use clearTimeout to simplify your code. Here's a example of how you can simplify it:

var ButtonCycler = (function () {
    var ButtonCycler = {};
    var autoCycle;
    var idx = 0;
    var buttonIds = [];
    var timer;
    buttonIds[0] = "id1";
    buttonIds[1] = "id2";
    //"Public" methods
    ButtonCycler.startAutoCycle = function () {
        cycleButtons();
    }
    ButtonCycler.stopAutoCycle = function () {
        clearTimeout(timer);
    }
    //Worker function (effectively private because it's in the closure)
    function cycleButtons() {
        var radioButton;
        radioButton = document.getElementById(buttonIds[idx]);
        radioButton.checked = true;
        idx = (idx < buttonIds.length - 1) ? idx + 1 : 0;

        timer = setTimeout(function () {
            cycleButtons();
        }, 2500); //Delay for 2.5 seconds      
    }
    return ButtonCycler;
})(); //IIFE

Upvotes: 0

Related Questions