javascript: Limpar todos os tempos limite?

99

Existe uma maneira de limpar todos os tempos limite de uma determinada janela? Suponho que os tempos limites estejam armazenados em algum lugar do windowobjeto, mas não foi possível confirmar isso.

Qualquer solução cross-browser é bem-vinda.

marcio
fonte
6
Cara, essa pergunta é de 3 anos atrás e tem ótimas respostas nela. Não há necessidade de mexer com isso.
marcio
Eu procurei por isso e encontrei os dois. O seu era mais velho, então achei que seria uma boa organização reduzir as duas perguntas a uma. O meu é apenas um voto; mais algumas pessoas precisarão votar para fechar, e o link duplicado pode ajudar outras pessoas.
Bernhard Hofmann
2
@Bernard Hofmann Não tenho certeza se conta como uma pergunta duplicada se esta obteve uma resposta melhor. A resposta aceita naquela de 2010 foi "Não".
RoboticRenaissance

Respostas:

148

Eles não estão no objeto janela, mas têm ids, que (afaik) são inteiros consecutivos.

Portanto, você pode limpar todos os tempos limite como:

var id = window.setTimeout(function() {}, 0);

while (id--) {
    window.clearTimeout(id); // will do nothing if no timeout with id is present
}
user123444555621
fonte
1
Isso é parte da especificação ou pode ser dependente da implementação?
Tikhon Jelvis de
7
Não precisa começar em 1. A especificação HTML5 diz: "Deixe que o identificador seja um número inteiro definido pelo agente do usuário maior que zero que identificará o tempo limite a ser definido por esta chamada." o que deixa espaço para o identificador ser qualquer número inteiro positivo, incluindo inteiros não consecutivos e não pequenos.
Mike Samuel
3
Isso é muito inteligente, mas o OP provavelmente deve perguntar se há uma solução melhor para controlar os temporizadores ativos.
Jason Harwig
3
Não é um loop infinito? Nem todos os navegadores renderizam if (0) como falso.
Travis J de
2
Já que perguntei como limpar TODOS os tempos limite, estou aceitando esta resposta. Solução muito inteligente.
marcio
79

Acho que a maneira mais fácil de fazer isso seria armazenar todos os setTimeoutidentificadores em um array, onde você pode iterar facilmente clearTimeout()em todos eles.

var timeouts = [];
timeouts.push(setTimeout(function(){alert(1);}, 200));
timeouts.push(setTimeout(function(){alert(2);}, 300));
timeouts.push(setTimeout(function(){alert(3);}, 400));

for (var i=0; i<timeouts.length; i++) {
  clearTimeout(timeouts[i]);
}
Michael Berkowski
fonte
14
Isso tem o bônus (ou potencial desvantagem, eu acho) de limpar apenas aqueles que você definiu, de forma que você não quebrará o código externo inadvertidamente.
Tikhon Jelvis de
1
+1, não apagará todos os tempos limite, mas é uma boa solução para limpar um grupo específico de tempos limite de uma vez
marcio
1
Esta é a melhor solução porque não vai quebrar o código externo, mas como eu perguntei como limpar todos os timeouts acabei aceitando @ Pumbaa80 resposta :)
marcio
2
@marcioAlmada Estranho, mas legal ver você retornar para comentar sobre isso novamente 2,5 anos depois :)
Michael Berkowski
6
Talvez eu queira limpar todos os tempos limite e intervalos quando entro em uma página da web que não controlo, porque eles têm um anúncio pop-up irritante que não começa até que meu adblocker seja executado.
RoboticRenaissance de
12

Eu tenho uma adição à resposta do Pumbaa80 que pode ser útil para alguém desenvolvendo para IEs antigos.

Sim, todos os principais navegadores implementam ids de tempo limite como inteiros consecutivos (o que não é exigido pela especificação ). Embora o número inicial seja diferente de navegador para navegador. Parece que Opera, Safari, Chrome e IE> 8 iniciam ids de tempo limite de 1, navegadores baseados no Gecko de 2 e IE <= 8 de algum número aleatório que é salvo magicamente na atualização da guia. Você pode descobrir sozinho .

Tudo isso indica que no IE <= 8 o while (lastTimeoutId--)ciclo pode ser executado mais de 8 dígitos e mostrar a mensagem " Um script nesta página está fazendo com que o Internet Explorer seja executado lentamente ". Portanto, se você não puder salvar todos os ids de timeout ou não quiser substituir window.setTimeout, você pode considerar salvar o primeiro id de timeout em uma página e limpar os tempos limites até ele.

Execute o código no carregamento inicial da página:

var clearAllTimeouts = (function () {
    var noop = function () {},
        firstId = window.setTimeout(noop, 0);
    return function () {
        var lastId = window.setTimeout(noop, 0);
        console.log('Removing', lastId - firstId, 'timeout handlers');
        while (firstId != lastId)
            window.clearTimeout(++firstId);
    };
});

E, em seguida, limpe todos os tempos limite pendentes que provavelmente foram definidos por código estrangeiro tantas vezes que você deseja

Kolya Ay
fonte
mais um para esclarecer algumas informações mais detalhadas.
Sanjay
8

Que tal armazenar os ids de tempo limite em uma matriz global e definir um método para delegar a chamada de função para a janela.

GLOBAL={
    timeouts : [],//global timeout id arrays
    setTimeout : function(code,number){
        this.timeouts.push(setTimeout(code,number));
    },
    clearAllTimeout :function(){
        for (var i=0; i<this.timeouts.length; i++) {
            window.clearTimeout(this.timeouts[i]); // clear all the timeouts
        }
        this.timeouts= [];//empty the id array
    }
};
Jaskey
fonte
3

Você deve reescrever o window.setTimeoutmétodo e salvar seu ID de tempo limite.

const timeouts = [];
const originalTimeoutFn = window.setTimeout;

window.setTimeout = function(fun, delay) { //this is over-writing the original method
  const t = originalTimeoutFn(fn, delay);
  timeouts.push(t);
}

function clearTimeouts(){
  while(timeouts.length){
    clearTimeout(timeouts.pop();
  }
}
convidado
fonte
3

Para limpar todos os tempos limite, eles devem ser "capturados" primeiro :

Coloque o código abaixo antes de qualquer outro script e ele criará uma função de wrapper para o setTimeout& original clearTimeout.

Um novo clearTimeoutsmétodo será adicionado ao windowobjeto, o que permitirá limpar todos os tempos limite (pendentes) ( link Gist ).

Outras respostas não têm suporte completo para possíveis argumentos setTimeout podem receber.

// isolated layer wrapper (for the local variables)
(function(_W){

var cache = [],                // will store all timeouts IDs
    _set = _W.setTimeout,      // save original reference
    _clear = _W.clearTimeout  // save original reference

// Wrap original setTimeout with a function 
_W.setTimeout = function( CB, duration, arg ){
    // also, wrap the callback, so the cache reference will be removed 
    // when the timeout has reached (fired the callback)
    var id = _set(function(){
        CB.apply(null, arguments)
        removeCacheItem(id)
    }, duration || 0, arg)

    cache.push( id ) // store reference in the cache array

    // id reference must be returned to be able to clear it 
    return id
}

// Wrap original clearTimeout with a function 
_W.clearTimeout = function( id ){
    _clear(id)
    removeCacheItem(id)
}

// Add a custom function named "clearTimeouts" to the "window" object
_W.clearTimeouts = function(){
    console.log("Clearing " + cache.length + " timeouts")
    cache.forEach(n => _clear(n))
    cache.length = []
}

// removes a specific id from the cache array 
function removeCacheItem( id ){
    var idx = cache.indexOf(id)

    if( idx > -1 )
        cache = cache.filter(n => n != id )
}

})(window);
vsync
fonte
2

Para completar, gostaria de postar uma solução geral que abrange ambos setTimeoute setInterval.

Parece que os navegadores podem usar o mesmo pool de IDs para ambos, mas de algumas das respostas para ClearTimeout e clearInterval o mesmo? , não está claro se é seguro confiar clearTimeoute clearIntervalexecutar a mesma função ou apenas trabalhar em seus respectivos tipos de temporizador.

Portanto, quando o objetivo é eliminar todos os tempos limite e intervalos, aqui está uma implementação que pode ser um pouco mais defensiva entre as implementações quando não é possível testar todos eles:

function clearAll(windowObject) {
  var id = Math.max(
    windowObject.setInterval(noop, 1000),
    windowObject.setTimeout(noop, 1000)
  );

  while (id--) {
    windowObject.clearTimeout(id);
    windowObject.clearInterval(id);
  }

  function noop(){}
}

Você pode usá-lo para limpar todos os temporizadores na janela atual:

clearAll(window);

Ou você pode usá-lo para limpar todos os temporizadores em um iframe:

clearAll(document.querySelector("iframe").contentWindow);
Chris Calo
fonte
tenha em mente que essa abordagem destrói o observador do vue-cli-service, portanto, você não pode fazer o hot-reload ao limpar todos os tempos limite e intervalos de tempo. Suponho que seja o mesmo para outros observadores de recarga quente para frameworks como (angular, react, ember etc)
Michail Michailidis
1

Eu uso Vue com Typescript.

    private setTimeoutN;
    private setTimeoutS = [];

    public myTimeoutStart() {

        this.myTimeoutStop();//stop All my timeouts

        this.setTimeoutN = window.setTimeout( () => {
            console.log('setTimeout');
        }, 2000);

        this.setTimeoutS.push(this.setTimeoutN)//add THIS timeout ID in array

    }

    public myTimeoutStop() {

        if( this.setTimeoutS.length > 0 ) {
            for (let id in this.setTimeoutS) {
                console.log(this.setTimeoutS[id]);
                clearTimeout(this.setTimeoutS[id]);
            }
            this.setTimeoutS = [];//clear IDs array
        }
    }
LuanLuanLuan
fonte
0

Use um tempo limite global do qual todas as outras funções derivam do tempo. Isso fará com que tudo seja executado mais rápido e mais fácil de gerenciar, embora adicione alguma abstração ao seu código.

Travis J
fonte
Eu não te entendo totalmente. Você quer dizer estender o comportamento da set_timeoutfunção global? Você poderia dar um exemplo de código?
marcio
1
Só quis dizer que você tem um tempo limite global em execução a cada 50 ms. Todas as outras funções que exigiriam um elemento de tempo, então, o pegariam do tempo limite global. O Google mudou para isso para eficiência, embora eu não consiga mais encontrar o artigo que cita isso.
Travis J de
0

Acabamos de publicar um pacote que resolve exatamente esse problema.

npm install time-events-manager

Com isso, você pode ver todos os tempos limite e intervalos por meio de timeoutCollection& intervalCollectionobjetos. Há também uma removeAllfunção que limpa todos os tempos limite / intervalos da coleção e do navegador.

Bar Goldinfeld
fonte
0

Já é muito tarde ... mas:

Basicamente, os IDs setTimeout / setInterval vão em ordem consecutiva, portanto, basta criar uma função de tempo limite fictício para obter o ID mais alto e limpar o intervalo em todos os IDs menores que esse.

const highestId = window.setTimeout(() => {
  for (let i = highestId; i >= 0; i--) {
    window.clearInterval(i);
  }
}, 0);
charri
fonte