2013-11-14 1 views
0

Я работаю над довольно большим приложением, мне нужно снова вызвать функцию заново, пока нажата клавиша с определенным интервалом. Есть части приложения, которые я не могу редактировать, но он заменяет мои прослушиватели .onkeyup(), и иногда интервал просто остается навсегда. Что я действительно хочу, чтобы интервал был остановлен, когда объект будет уничтожен, переназначен и т. Д. После того, как setInterval(), привязки, замыкания, я сделал это, чтобы попробовать что-то, и теперь я еще более запутался:Как убить setInterval()/setTimout(), если я потеряю вызывающий объект?

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 
... 

И так далее forverer; Интервал никогда не останавливается ...

  1. Может ли кто-нибудь объяснить?

  2. Есть ли общее, изящное решение для обеспечения того, чтобы я не переходил с интервалом в эфир?

+0

'Интервал никогда не останавливается ... Может кто-нибудь объяснить,' Обновление моего ответа на этот вопрос решить – HMR

ответ

1

Интервал никогда не останавливается ... Может кто-нибудь объяснить?

Переменные запертые в области замыкания является причиной того, следующий код будет использовать переменную O в той же области, которая устанавливает о пустое значение.

Примечание: Я предпочитаю использовать создателей закрытий для ограничения объема и предотвращения других сюрпризов. Следующее создаст закрытие, на котором они летают, чтобы сохранить код простым.

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

Следующий код будет использовать, как о прошло о, прошел о теперь ловушку в созданной области закрытия и установки о пустом значении не влияет на переданные о. Мутирование o (o.something = 22) повлияет на пройденный o.(Google «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); 
} 

Поскольку имеющий переменный я в том же объеме, что и закрытие даст вам нежелательные результаты:

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); 
+0

Вот оно. Понимаете, я знаю, что мы должны придерживаться наших рекомендаций, но это приложение в основном Ajax, и у меня нет разрешения на беспорядок с помощью back-end прямо сейчас. Приложение отбрасывало .onkeyup, .onkeydown и очищало пространство имен, в котором я находилось, и мучительно контролировал интервал из моих рук. Я не хотел добавлять конкретный клоч для каждого случая, который мог найти. Поэтому у меня будет интервал, который выключается, и местный слушатель, который возвращает его обратно, когда он выключается. Таким образом, если локальное пространство имен будет взорвано, интервал выйдет из строя. Я думаю? – TGIWhiskey

+0

@TGIWhiskey Правильно, когда 'this.doContinue' является ложным, тогда' this.timeoutid' будет установлено в false вместо идентификатора timeoutID, который был бы возвращен установкой тайм-аута, который снова вызовет this.go. Таким образом, он перестанет делать что-либо, пока вы не вызовете снова (ничего не останется в памяти, потому что не создается новое закрытие). При вызове «go» несколько раз он выкидывает (очищает) таймаут, заданный первым вызовом, и устанавливает новый, поэтому вы не будете одновременно запускать несколько тайм-аутов. – HMR

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

1. Вы должны объявить переменные с вар (или пусть) или они стали глобальными?. 2. Почему бы не воспользоваться прототипом? 3. При создании замыканий на лету, как будто вы могли бы включать в себя, возможно, большие переменные, которые вам никогда не нужны, но попасть в ловушку там. – HMR

+0

@HMR 1.Thats, используемый OP, поэтому я не модифицировал его, чтобы сделать его локальным. 2. Если вы используете прототип, тогда все объекты будут иметь одну и ту же ссылочную переменную таймера, разделяемую между ними, тогда как вы остановите только того, кого вы хотите. Его не так, как будто он окажется там в ловушке. – Shadow

+0

1. Хорошо, я бы сказал, что это была опечатка или что-то в этом роде. При создании нескольких экземпляров 'window.that' будет перезаписан, а ваша точка 2 будет недействительной, поскольку код как есть; разбивается на несколько экземпляров. 2. Нет, нет, если вы сделаете это правильно. 3. Я бы не стал рассчитывать на это. Я добавлю общую функцию createClosure в мое введение в ответ функции конструктора. По честному; Обычно я не предлагаю «сохранить создание закрытия» во многих примерах кода, но вы должны, поскольку никогда не знаете, что и где вы собираетесь добавлять в свой код. – HMR

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); 
Смежные вопросы