problemas setTimeout / clearTimeout

103

Tento fazer uma página para ir para a página inicial após, por exemplo. 10seg de inatividade (o usuário não clica em qualquer lugar). Eu uso jQuery para o resto, mas o set / clear na minha função de teste é puro javascript.

Em minha frustração, acabei com algo parecido com essa função que esperava poder chamar a qualquer clique na página. O cronômetro começa bem, mas não é zerado com um clique. Se a função for chamada 5 vezes nos primeiros 10 segundos, 5 alertas aparecerão ... sem clearTimeout ...

function endAndStartTimer() {
    window.clearTimeout(timer);
    var timer;
    //var millisecBeforeRedirect = 10000; 
    timer = window.setTimeout(function(){alert('Hello!');},10000); 
}

Alguém tem algumas linhas de código que farão o truque? - em qualquer clique para parar, redefinir e iniciar o cronômetro. - Quando o cronômetro atinge, por exemplo. 10sec fazer alguma coisa.

Tillebeck
fonte

Respostas:

226

Você precisa declarar timer fora da função. Caso contrário, você obtém uma variável totalmente nova em cada invocação de função.

var timer;
function endAndStartTimer() {
  window.clearTimeout(timer);
  //var millisecBeforeRedirect = 10000; 
  timer = window.setTimeout(function(){alert('Hello!');},10000); 
}
Pontudo
fonte
mais 1 porque é necessário, neste caso, limpar a variável que encobre o timeOut antes de chamar o timeout, evitando que seja invocado duas vezes
Diego Favero
46

O problema é que a timervariável é local e seu valor é perdido após cada chamada de função.

Você precisa persistir, pode colocá-la fora da função ou, se não quiser expor a variável como global, pode armazená-la em um encerramento , por exemplo:

var endAndStartTimer = (function () {
  var timer; // variable persisted here
  return function () {
    window.clearTimeout(timer);
    //var millisecBeforeRedirect = 10000; 
    timer = window.setTimeout(function(){alert('Hello!');},10000); 
  };
})();
CMS
fonte
15

Isso ocorre porque o temporizador é uma variável local para sua função.

Tente criá-lo fora da função.

arco voltaico
fonte
6

Uma maneira de usar isso para reagir:

class Timeout extends Component {
  constructor(props){
    super(props)

    this.state = {
      timeout: null
    }

  }

  userTimeout(){
    const { timeout } = this.state;
    clearTimeout(timeout);
    this.setState({
      timeout: setTimeout(() => {this.callAPI()}, 250)
    })

  }
}

Útil se você quiser chamar uma API apenas depois que o usuário parar de digitar, por exemplo. A função userTimeout pode ser vinculada por meio de onKeyUp a uma entrada.

zero_cool
fonte
1
É isso que estou procurando há algumas horas, obrigado. Eu estava pensando se existe uma maneira melhor para esse tipo de resultado acontecer.
Nikasv
1
@nikasv throttling e debouncing são duas alternativas: medium.com/@_jh3y/…
zero_cool
2

Não tenho certeza se isso viola alguma regra de codificação de boas práticas, mas geralmente digo esta:

if(typeof __t == 'undefined')
        __t = 0;
clearTimeout(__t);
__t = setTimeout(callback, 1000);

Isso evita a necessidade de declarar o temporizador fora da função.

EDIT: também não declara uma nova variável a cada invocação, mas sempre recicla a mesma.

Espero que isto ajude.

clamiax
fonte
0

Isso funciona bem. É um gerente que criei para lidar com eventos de retenção. Tem eventos para segurar e para quando você deixar ir.

function onUserHold(element, func, hold, clearfunc) {
    //var holdTime = 0;
    var holdTimeout;

    element.addEventListener('mousedown', function(e) {
        holdTimeout = setTimeout(function() {
            func();
            clearTimeout(holdTimeout);
            holdTime = 0;
        }, hold);
        //alert('UU');
    });

    element.addEventListener('mouseup', clearTime);
    element.addEventListener('mouseout', clearTime);

    function clearTime() {
        clearTimeout(holdTimeout);
        holdTime = 0;
        if(clearfunc) {
            clearfunc();
        }
    }
}

O parâmetro do elemento é aquele que você mantém. O parâmetro func é acionado quando é mantido por um número de milissegundos especificado pelo parâmetro hold. O parâmetro clearfunc é opcional e se for fornecido, será disparado se o usuário soltar ou deixar o elemento. Você também pode fazer algumas soluções alternativas para obter os recursos que deseja. Aproveitar! :)

Kino Bacaltos
fonte
0

Exemplo prático usando Jquery para um menu suspenso! Ao passar o mouse sobre #IconLoggedinUxExternal mostra div # ExternalMenuLogin e defina o tempo limite para ocultar o div # ExternalMenuLogin

Ao passar o mouse sobre div # ExternalMenuLogin, ele cancela o tempo limite. Ao sair do mouse em div # ExternalMenuLogin, ele define o tempo limite.

O objetivo aqui é sempre invocar clearTimeout antes de definir o tempo limite, evitando chamadas duplas

var ExternalMenuLoginTO;
$('#IconLoggedinUxExternal').on('mouseover mouseenter', function () {

    clearTimeout( ExternalMenuLoginTO )
    $("#ExternalMenuLogin").show()
});

$('#IconLoggedinUxExternal').on('mouseleave mouseout', function () {

    clearTimeout( ExternalMenuLoginTO )    
    ExternalMenuLoginTO = setTimeout(
        function () {

            $("#ExternalMenuLogin").hide()

        }
        ,1000
    );
    $("#ExternalMenuLogin").show()
});

$('#ExternalMenuLogin').on('mouseover mouseenter', function () {

    clearTimeout( ExternalMenuLoginTO )
});
$('#ExternalMenuLogin').on('mouseleave mouseout', function () {

    clearTimeout( ExternalMenuLoginTO )
    ExternalMenuLoginTO = setTimeout(
        function () {

            $("#ExternalMenuLogin").hide()

        }
        ,500
    );
});
Diego Favero
fonte