如果丢失调用对象,如何终止setInterval()/ setTimout()?

时间:2013-11-14 05:42:29

标签: javascript binding closures settimeout setinterval

我正在开发一个相当大的应用程序,我需要在一个特定的时间间隔按下一个键时重新调用一个函数。应用程序的部分内容我无法编辑,但它正在替换我的.onkeyup()侦听器,有时间隔只是永远留在那里。我真正想要的是在对象被破坏,重新分配等时间隔停止...在setInterval(),绑定,闭包之后,我做了这个以尝试一些东西,现在我甚至更困惑:

function myInterval(){
    this.name = 'Paul'
    that=this;
this.go = function go(){
        if(that){
            this.interval = setTimeout(function(){
                console.log(that.name);
                that.go();
            },100);
        };
        console.log(that.name);
    };
};

periodic = new myInterval();
periodic.go();
setTimeout(function(){
    periodic.name='George';
}, 200);
setTimeout(function(){
    periodic.name ='John';
    periodic.go = '';
    periodic.name = 'Ringo'
}, 300);

输出:

Paul
Paul
Paul
George
George
Ringo 
> Uncaught TypeError: Property 'go' of object #<myInterval> is not a function

因此,重新分配该功能是有效的,但如果我更换

periodic.go = '';

periodic = '';

我得到了

Paul
Paul
Paul
George
George
John
John
...

依此类推;间隔永不停止......

  1. 任何人都可以解释一下吗?

  2. 是否有一个共同的,优雅的解决方案,以确保我不会将运行间隔减少到以太?

3 个答案:

答案 0 :(得分:2)

var timer = setTimeout(func,100)

清除它

if(timer)
clearTimeout(timer)

如果是间隔

var timer = setInterval(func,100)

清除它

if(timer)
clearInterval(timer)

EX

function myInterval(){
    this.name = 'Paul'
    that=this;

    this.go = function go(){
        if(that){
            this.interval = setTimeout(function(){
                console.log(that.name);
                that.go();
            },100);
        };
        console.log(that.name);
    };

    this.stop = function(){ if(this.interval) clearInterval(this.interval);};
};

答案 1 :(得分:1)

  
    

间隔永不停止......任何人都可以解释一下吗?

  

在闭包范围内捕获的变量就是这样的原因,下面的代码将在同一范围内使用变量o将o设置为null。

注意:我更喜欢使用闭包创建者来限制范围并防止其他意外。以下将创建闭包,以保持代码简单。

var o = {};
setTimeout(function(){//closure accessing o directly
   console.log("why is o null here:",o);
},100);
o=null;

以下代码将使用o作为传递o,传递o现在被捕获在创建的闭包范围中,将o设置为null不会影响传递的o。变异o(o.something = 22)将影响传递o。 (谷歌“按价值参考javascript”参考)

var o = {};
setTimeout((function(o){
  return function(){//closure accessing passed o
   console.log("why is o not here:",o);
  };
}(o)),100);
o=null;

要解决循环创建closurs中的常见问题,请将变量(i)传递给返回闭包的函数

for(var i = 0;i<10;i++){
  setTimeout((function(i){
    return function(){//not using i in the for loop but the passed one
     console.log("and i is:",i);//0 to 9
    };
  }(i)),100);
}

因为将i变量放在与闭包相同的范围内会给你带来不必要的结果:

for(var i = 0;i<10;i++){
  setTimeout(function(){
    console.log("and i is:",i);//10 times and i is: 10
  },100);
}

为什么periodic.go =“”的工作与通过引用传递值有关。它的工作原理如下代码所示:

function test(o){
  o=22;//no mutation but an assignment
}
var o = {name:"me"};
test(o);
console.log(o);//Object { name="me" }

function test2(o){
  o.name="you";//mutates
}
test2(o);
console.log(o);//Object { name="you"}

如何解决

我已经改变了你的代码以利用共享成员的protype(go函数)并创建闭包以确保闭包的范围仅限于你实际需要的内容。

有关详细信息,请阅读introduction to constructor function and the this variable

function MyInterval(){//capitalize constructor
    this.name = 'Paul';
    this.doContinue = true;
    this.timeoutid = false;
};
MyInterval.prototype.closures ={//creates closures with limited scope
  //closure for the go function setting the invoking object
  go:function(me){
    return function(){
      console.log("In go, name is:",me.name);
      me.go();
      //de reference "me", we no longer need it
      // can't do this in a setInterval though
      me=null;
    };
  }
}
MyInterval.prototype.go = function(){
  if(this.constructor===MyInterval){
    //if you were to call go multiple times
    if(this.timeoutid)
      clearTimeout(this.timeoutid);
    //do it again if this.doContinue is true
    this.timeoutid = (this.doContinue)? setTimeout(
      this.closures.go(this)
      //creates a closure function
      ,100
    ):false;
    return;
  };
  console.log("this is not the correct value:",this);
};
//added stop, good call from shadow, now you can stop immediately
//  or set doContinue to false and let it run it's course
MyInterval.prototype.stop = function(){
  if(this.timeoutid)
    clearTimeout(this.timeoutid);
  this.timeoutid=false;
};

periodic = new MyInterval();
periodic.go();
//because the clearTimeout you can accedentally do something silly like:
periodic.go();
periodic.go();
setTimeout(function(){
    periodic.name='George';
}, 150);
setTimeout(function(){
    periodic.name ='John';
    periodic.doContinue = false;
    periodic.name = 'Ringo'
}, 250);

答案 2 :(得分:1)

在您的代码中:

function myInterval(){
    this.name = 'Paul'
    that=this;

当你第一次调用 myInterval 并且执行上面的行时,变成了全局,我认为你的意思是保持本地化。否则, 将引用最后创建的实例,这不一定是“当前”实例。

无论如何,执行此操作的最佳方法是为实例提供 stop 方法。所以你这样做:

// By convention, constructors start with a capital letter
function MyInterval() {

    // Use a more suitable name than "that" to reference the instance
    var interval = this;
    this.name = 'Paul'

    // This keeps calling itself every 100ms
    this.go = function go(){

      // Keep a reference to the timeout so it can be cancelled
      this.timeout = setTimeout(function(){
                       console.log('setTimeout: ' + interval.name);
                       interval.go();
                     }, 100);
      console.log('Go: ' + interval.name);
    };

    // Stop the timeout
    this.stop = function() {  
      if (interval.timeout) {
        clearTimeout(interval.timeout);
      }
    };
};

var periodic = new MyInterval();

// In 100 ms, it will log its name
periodic.go();

// In 200 ms its name will be changed
setTimeout(function(){
    periodic.name='George';
}, 200);

// In 500 ms its name will be changed and it will be cancelled.
setTimeout(function(){
    periodic.name ='John';

    // call stop here
    periodic.stop();
    periodic.name = 'Ringo'
}, 500);