Dark mode
Light mode
31 Mar 20
7 min

Como criar um Timer com resume(), pause() e reset() usando JavaScript

Um componente muito útil para auxiliar em slideshows, carousels, animações, etc.

Introdução

Eaí galera! Nesses tempos de crise mundial acabei me perdendo um pouco na agenda de artigos que tinha planejado em escrever.

Espero que a maioria de vocês esteja em casa, seguros e aproveitando o tempo para aprender coisas novas ou melhorar suas habilidades.

Felizmente as coisas já estão voltando ao normal por aqui e estou conseguindo me organizar melhor.

Nesse artigo, quero mostrar para vocês como criar um componente bastante útil, e que uso em quase todos os sites que faço, vou ensinar a criar um timer.

O que é um Timer?

Um timer (ou temporizador) é um objeto usado para rodar uma função a cada intervalo X de tempo.

Antes de começar

Primeiro, quero deixar claro para o que esse timer deve ser usado e para o que ele não deve ser usado.

Quando usar

Use para controlar componentes como slideshows, carousel, rodar animações, etc. Resumindo, lugares em que você precise controlar apresentação, onde o timer serve para te auxiliar nesse objetivo.

Quando não usar

Não use esse timer se você depende que o tempo seja preciso.

Por que o Timer não é preciso?

Como o JavaScript no navegador é executado em uma única thread, cada bloco de código espera o bloco anterior, causando um atraso de alguns milissegundos.

Ou seja, para cada execução da função do seu timer, o JavaScript vai aguardar um pouquinho antes de executar a próxima função. Esse tempo depende do que a sua função faz, do intervalo de execução e do poder de processamento da CPU do usuário.

Com o passar do tempo, esses milissegundos vão acumulando e começam a fazer uma diferença enorme no resultado final.

Em detalhes

Esse JSFiddle demonstra a diferença entre as funções setTimeout e setInterval, que são as duas funções que controlam tempo de execução no JavaScript.

Se você esperar alguns minutos vai ver que o número de vezes que cada função é chamada vai se diferenciando muito.

De qualquer maneira, nenhuma das duas mantém a precisão de uma chamada por segundo, como é esperado.

Esse artigo do John Resig, criador da jQuery, explica com mais detalhes como isso funciona no caso de se interessar.

Criando o nosso Timer

Primeiro vamos declarar o objeto que vai receber uma função callback e o tempo de duração do timer, que vou chamar de delay.

1function Timer(callback, delay) {}

Note que estamos criando um objeto, e não uma função. Se você ainda não dominou muito bem o conceito de objeto em JavaScript, dá uma olhada nessa documentação.

Nota
Hoje em dia podemos fazer isso usando classes também, mas preferi fazer uma solução com suporte pra todos os navegadores.

Instanciando o nosso Timer

Agora vamos inicializar o timer, assim você pode ir testando os métodos que iremos implementar:

1function Timer(callback, delay) {} 2 3var timer = new Timer(function () { 4 console.log("Chamou a função!") 5}, 3000)

Método resume()

Vamos começar pela função que inicia o timer. A primeira coisa a fazer é usar a função setTimeout presente na web API dos navegadores.

Essa função recebe como parâmetros uma função e o tempo que se deve esperar para executar tal função, e retorna um ID, que vamos guardar para usar mais tarde.

Mais informações sobre a setTimeout você encontra na documentação.

1function Timer(callback, delay) { 2 var timerId 3 4 var resume = function () { 5 timerId = window.setTimeout(function () { 6 resume() 7 callback() 8 }, delay) 9 } 10} 11 12var timer = new Timer(function () { 13 console.log("Chamou a função!") 14}, 3000)

Aqui estamos criando uma função recursiva - função que chama ela mesma - nomeada resume().

Ou seja, a cada intervalo de tempo, ou delay, a função resume() chama a si mesma e em seguida a nossa função de callback(), fazendo com que essa seja executada infinitamente, a cada intervalo.

Porém, no momento ainda não está funcionando, precisamos fazer com que a função resume() seja um método do objeto e então chamá-la.

1function Timer(callback, delay) { 2 var timerId 3 4 var resume = function () { 5 timerId = window.setTimeout(function () { 6 resume() 7 callback() 8 }, delay) 9 } 10 this.resume = resume 11} 12 13var timer = new Timer(function () { 14 console.log("Chamou a função!") 15}, 3000) 16timer.resume()

Note que foram adicionadas as linhas:

  • this.resume = resume que faz com que a função resume deixe de ser privada para ser pública, podendo ser invocada pelo objeto.
  • timer.resume() que efetivamente chama o método resume(), iniciando o nosso timer, nesse ponto, você deveria ver no seu console uma mensagem sendo exibida a cada 3 segundos.

Método pause()

Muito bem, agora vamos complicar um pouquinho e adicionar o método pause():

1function Timer(callback, delay) { 2 var timerId 3 4 this.pause = function () { 5 window.clearTimeout(timerId) 6 } 7 8 var resume = function () { 9 timerId = window.setTimeout(function () { 10 resume() 11 callback() 12 }, delay) 13 } 14 this.resume = resume 15} 16 17var timer = new Timer(function () { 18 console.log("Chamou a função!") 19}, 3000) 20timer.resume()

Problema 1

A primeira a coisa a fazer no método pause é... parar tudo! Por mais surpreendente que possa parecer, é isso que temos de fazer.

Então nós temos o nosso timeout rodando na função resume(), certo? Pra pará-lo precisamos removê-lo completamente, e fazemos isso usando a função clearTimeout() da web API.

Ela recebe como parâmetro exatamente o ID retornado do timeout que desejamos parar.

Assim, resolvemos o primeiro problema, paramos o nosso timer.

Problema 2

Beleza, mas ainda temos o seguinte problema: se chamamos a função resume() novamente, ela não vai começar de onde parou, mas vai criar um novo timeout iniciando do nosso delay de novo!

Não é isso o que queremos, a gente quer que o timer volte a rodar de onde parou.

Para isso, vamos precisar adicionar algumas variáveis novas e fazer algumas modificações no método resume() e pause():

1function Timer(callback, delay) { 2 var timerId 3 var start 4 var remaining = delay 5 6 this.pause = function () { 7 window.clearTimeout(timerId) 8 remaining -= new Date() - start 9 } 10 11 var resume = function () { 12 start = new Date() 13 timerId = window.setTimeout(function () { 14 remaining = delay 15 resume() 16 callback() 17 }, remaining) 18 } 19 this.resume = resume 20} 21 22var timer = new Timer(function () { 23 console.log("Chamou a função!") 24}, 3000) 25timer.resume()

Calma calma... eu sei que foram várias modificações. Vamos passar por cada uma delas pra você entender direitinho:

Novas variáveis

  • start vai ser usada para marcar quando a função resume() foi chamada.
  • remaining vai ser usada para marcar o tempo que falta para o timer terminar de rodar.

Alterações em resume()

  • Adicionamos a linha start = new Date() que justamente guarda a hora, minuto, segundo e milissegundo que a função resume() foi chamada.
  • Substituímos a variável delay da chamada de setTimeout pela variável remaining, isso porque queremos que ao chamar resume() depois de chamarmos pause(), o novo timeout tenha o tenha o tempo que falta e não recomece do tempo total.
  • Finalmente, adicionamos a linha remaining = delay dentro do timeout, porque assim que ele acabar, o novo timeout deve começar do tempo total novamente.

Eu sei que é muita informação, tente pensar em como o algoritmo está funcionando e leia de novo se achar necessário.

Alterações em pause()

  • Adicionamos a linha remaining -= new Date() - start que justamente faz a conta do tempo restante, subtraindo do tempo restante a diferença entre a hora atual - new Date() - da hora em que chamamos resume(), ou seja, a variável start.

Agora sim, quando chamamos pause() e resume() novamente, o timer volta a funcionar a partir de onde parou.

Método reset()

Finalmente, adicionamos o último método:

1function Timer(callback, delay) { 2 var timerId 3 var start 4 var remaining = delay 5 6 this.pause = function () { 7 window.clearTimeout(timerId) 8 remaining -= new Date() - start 9 } 10 11 var resume = function () { 12 start = new Date() 13 timerId = window.setTimeout(function () { 14 remaining = delay 15 resume() 16 callback() 17 }, remaining) 18 } 19 this.resume = resume 20 21 this.reset = function () { 22 remaining = delay 23 } 24} 25 26var timer = new Timer(function () { 27 console.log("Chamou a função!") 28}, 3000) 29timer.resume()

Tudo o que ele faz é resetar a variável de tempo restante.

Tenha em mente que esse método por si só não reinicia o timer, ele apenas reseta.

Porém, com esses 3 métodos é possível reiniciar o timer só chamando timer.pause(), timer.reset() e timer.resume() nessa ordem.

Exemplo prático

Para demonstrar melhor como esse timer funciona, fiz esse pen:

Como eu disse lá no começo, esse objeto é muito útil para slideshows e carousels, porque se quisermos fazer eles avançarem sozinhos é só chamar o método que faz avançar de dentro do timer, ganhando outras funcionalidades para controlar melhor esses componentes com pause() e resume().

Conclusão

Espero que tenha gostado do post. Claro que você pode modificar esse código e melhorá-lo de acordo com as suas necessidades, nem sempre precisamos de todos os métodos ou usá-los da mesma forma. Porém, é assim que costumo usar nos meus componentes já tem algum tempo.

É isso aí galera, fique em casa e até o próximo artigo!

Compartilhe

Foto de Thiago sorrindo

Autor

Thiago Rossener

Desenvolvedor front-end, filósofo e espiritualista

Sua assinatura não pôde ser validada.
Você fez sua assinatura com sucesso.

Newsletter

Assinando minha newsletter você fica sabendo sempre que eu publicar algo novo, prometo que vai ser só isso.

Comentários