-->

setInterval fires only once

2019-09-06 06:27发布

问题:

I'm trying to create a self contained timer where all variables are inside a object.

For the most part it works but I can get this to fire once. What am I missing?

    function Countdown()
    {
        this.appId = null;
        this.timerId = null;
        this.seconds = null;

        this.decrementCounter = function (instant)
        {
            if (instant == null)
                return;

            instant.tick();
            if (instant.seconds === 0)
            {
                instant.tickEnd();
                instant.stop();
            }
            instant.seconds--;
        };
        this.tick = function ()
        {
            var xx = this.appId
        };
        this.tickEnd = function ()
        {
            var xx = this.appId
        };
        this.start = function ()
        {
            clearInterval(this.timerId);
            this.timerId = setInterval(this.decrementCounter(this), 1000);
        };
        this.stop = function ()
        {
            clearInterval(this.timerId);
        };
    }

回答1:

I modified your code a bit and changed the line containing setInterval to this:

this.timerId = setInterval((function(scope) {return function() {scope.decrementCounter(scope);};})(this), 1000);

The functions run inside of setInterval run in the window scope. It only runs once, because you don't pass the function itself just the result of it. You need to return the actual function or pass an anonymous function which calls it.

jsfiddle demo: http://jsfiddle.net/2gLdL/1/



回答2:

You are calling the function, not assigning a reference

this.timerId = setInterval(this.decrementCounter(this), 1000); 

Seems weird you are passing in "this" as an argument...

use bind()

this.timerId = setInterval(this.decrementCounter.bind(this, this), 1000); 

or a closure

var that = this;
this.timerId = setInterval(function(){that.decrementCounter(that); }, 1000); 


回答3:

In the following bit of code:

this.timerId = setInterval(this.decrementCounter(this), 1000);

You are executing this immediately:

this.decrementCounter(this)

And so the return value of that, is what is being called by setInterval each second. Usually you would want to pass a function closure, like this:

var _this = this; //make sure your variable is available in the scope that follows
this.timerId = setInterval(function() { this.decrementCounter(_this); }, 1000);

Then, when your timer is executed, it's the function which is called, and that function then executes what you want. The other option is to pass the first parameter as a string such as "callAFunction('blah')" which will be evaluated each second and executed, but I believe the above is that you want.



回答4:

Try this:

setInterval(function(){Countdown()},500);


回答5:

I provided an answer to another question that creates a simple stopwatch.

You could modify the code slightly to count down instead of up.

Here's a jsbin demo.


Original snippet

var Stopwatch = function(elem, options) {

  var timer       = createTimer(),
      startButton = createButton("start", start),
      stopButton  = createButton("stop", stop),
      resetButton = createButton("reset", reset),
      offset,
      clock,
      interval;

  // default options
  options = options || {};
  options.delay = options.delay || 1;

  // append elements     
  elem.appendChild(timer);
  elem.appendChild(startButton);
  elem.appendChild(stopButton);
  elem.appendChild(resetButton);

  // initialize
  reset();

  // private functions
  function createTimer() {
    return document.createElement("span");
  }

  function createButton(action, handler) {
    var a = document.createElement("a");
    a.href = "#" + action;
    a.innerHTML = action;
    a.addEventListener("click", function(event) {
      handler();
      event.preventDefault();
    });
    return a;
  }

  function start() {
    if (!interval) {
      offset   = Date.now();
      interval = setInterval(update, options.delay);
    }
  }

  function stop() {
    if (interval) {
      clearInterval(interval);
      interval = null;
    }
  }

  function reset() {
    clock = 0;
    render();
  }

  function update() {
    clock += delta();
    render();
  }

  function render() {
    timer.innerHTML = clock/1000; 
  }

  function delta() {
    var now = Date.now(),
        d   = now - offset;

    offset = now;
    return d;
  }

  // public API
  this.start  = start;
  this.stop   = stop;
  this.reset  = reset;
};