Тайм-аут JavaScript срабатывает 3 раза вместо одного раза (clearTimeout не работает?)

Я хочу запустить ajax-действие, когда пользователь делает паузу при наборе текста (а не после каждого нажатия). Поэтому я сделал что-то вроде этого:

Когда пользователь перестает печатать после 3 секунд выполнения незанятой функции, она должна выполняться... (это - но почему 3 раза для длинных фраз - я ожидаю, что она будет работать только один раз, так как я очищаю таймаут после каждого нажатия клавиши). В чем проблема?

var timer; 
var interval = 3000; 
$('#inp').keyup(function() {
  timer = setTimeout(done, interval); 
}); 

$('#inp').keydown(function() {
  clearTimeout(timer)  
}); 

function done() {
  console.log('ajax'); 
}

Рабочий пример на jsfiddle: http://jsfiddle.net/vtwVH/

Ответ 1

Проблема в том, что вы переписываете переменную timer в событие keydown.

Итак, если вы нажмете еще одну клавишу до того, как вычеркнет Timout, например, держите клавишу

Ссылка на timeOut потеряна, и вы не сможете ее снова очистить.

Чтобы исправить это, вы можете просто очистить и установить таймер в событии keyUp, например

var timer; 
var interval = 3000; 


$('#inp').keyup(function(e){ 
    if(timer) {
        clearTimeout(timer);
    }
    timer = setTimeout(done, interval); 

}); 

function done() {
    console.log('ajax'); 
}

Здесь работает fiddle

Ответ 2

Когда вы печатаете, вы можете иногда нажать еще одну клавишу, прежде чем отпускать первую. Вы можете этого не заметить, особенно если вы печатаете длинную фразу. В этом случае порядок событий не down up down up, а down down up up, и поэтому будут установлены два тайм-аута.

Более согласованно очищать таймаут непосредственно перед его установкой (в том же обработчике событий).

Ответ 3

События, похоже, не следуют строгим последовательным правилам, второй ввод ключей происходит раньше, чем первая клавиатура, поэтому таймер инициализируется несколько раз.

Попробуйте этот мод:

$('#inp').keyup(function(){
  clearTimeout(timer);
  timer = setTimeout(done, interval); 
}); 

Ответ 4

Просто убедитесь, что вы очистили интервал:

var timer; 
var interval = 3000; 


$('#inp').keyup(function(){
  clearTimeout(timer)  
  timer = setTimeout(done, interval); 
}); 

$('#inp').keydown(function(){
  clearTimeout(timer)  
}); 

function done() {
  console.log('ajax'); 
}

JSFiddle

Ответ 5

Если пользователь не набирает очень медленно, keydown и keyup не будут поступать так, как вы думаете. Например, вы можете получить два события keydown, за которыми следуют два события keyup. Если вы не справитесь с этим, второе событие keyup запустит другой таймер и перезапишет ссылку на первый таймер, чтобы он не очистился.

Следите за тем, выполняется ли тайм-аут, и очистите его в keyup, если он запущен:

var timer = null;
var interval = 3000; 
$('#inp').keyup(function(){
  if (timer != null) {
    window.clearTimeout(timer)  
  }
  timer = window.setTimeout(done, interval); 
}); 

$('#inp').keydown(function(){
  if (timer != null) {
    window.clearTimeout(timer);
    timer = null;
  }
}); 

function done() {
  timer = null;
  console.log('ajax'); 
}

Демо: http://jsfiddle.net/Guffa/vtwVH/3/