javascript: cancellare tutti i timeout?


99

C'è un modo per cancellare tutti i timeout da una determinata finestra? Suppongo che i timeout siano memorizzati da qualche parte windownell'oggetto ma non ho potuto confermarlo.

Qualsiasi soluzione cross browser è la benvenuta.



6
Amico, questa domanda è di 3 anni fa e contiene ottime risposte. Non c'è bisogno di scherzare con quello.
marcio

Ho cercato questo e ho trovato entrambi. La tua era più grande, quindi ho pensato che sarebbe stata una buona pulizia ridurre le due domande a una. Il mio è solo un voto; molte altre persone dovranno votare per chiudere e il collegamento duplicato potrebbe aiutare gli altri.
Bernhard Hofmann

2
@Bernard Hofmann Non sono sicuro se conta come una domanda duplicata se questa ha una risposta migliore. La risposta accettata a quella del 2010 è stata "No".
RoboticRenaissance

Risposte:


148

Non sono nell'oggetto finestra, ma hanno id, che (afaik) sono numeri interi consecutivi.

Quindi puoi cancellare tutti i timeout in questo modo:

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

while (id--) {
    window.clearTimeout(id); // will do nothing if no timeout with id is present
}

1
Fa parte della specifica o potrebbe dipendere dall'implementazione?
Tikhon Jelvis

7
Non è necessario che inizi da 1. La specifica HTML5 dice "Lascia che l'handle sia un numero intero definito dall'agente utente maggiore di zero che identificherà il timeout da impostare con questa chiamata". che lascia spazio all'handle per essere qualsiasi numero intero positivo inclusi numeri interi non consecutivi e non piccoli.
Mike Samuel

3
È molto intelligente, ma l'OP dovrebbe probabilmente chiedere se esiste una soluzione migliore che tenga traccia dei timer attivi.
Jason Harwig

3
Non è un ciclo infinito? Non tutti i browser visualizzano if (0) come false.
Travis J

2
Dato che ho chiesto come cancellare TUTTI i timeout, accetto questa risposta. Soluzione molto intelligente.
marcio

79

Penso che il modo più semplice per farlo sarebbe memorizzare tutti gli setTimeoutidentificatori in un array, dove puoi facilmente iterare clearTimeout()su tutti loro.

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]);
}

14
Questo ha il bonus (o il potenziale svantaggio, immagino) di cancellare solo quelli che hai impostato, in modo da non rompere inavvertitamente il codice esterno.
Tikhon Jelvis

1
+1, non cancellerà tutti i timeout ma è una buona soluzione per cancellare un gruppo specifico di timeout contemporaneamente
marcio

1
Questa è la soluzione migliore perché non romperà il codice esterno, ma da quando ho chiesto come cancellare tutti i timeout ho finito per accettare la risposta @ Pumbaa80 :)
marcio

2
@marcioAlmada Strano, ma è bello vederti tornare a commentare di nuovo su questo 2.5 anni dopo :)
Michael Berkowski

6
Forse mi piacerebbe cancellare tutti i timeout e gli intervalli quando entro in una pagina web che non controllo, perché hanno un fastidioso annuncio pop-up che non si avvia fino a quando il mio adblocker viene eseguito.
RoboticRenaissance

12

Ho un'aggiunta alla risposta di Pumbaa80 che potrebbe essere utile per qualcuno che sviluppa per i vecchi IE.

Sì, tutti i principali browser implementano gli ID di timeout come numeri interi consecutivi (che non è richiesto dalle specifiche ). Anche se il numero iniziale è diverso da browser a browser. Sembra che Opera, Safari, Chrome e IE> 8 avviino gli ID di timeout da 1, i browser basati su Gecko da 2 e IE <= 8 da un numero casuale che viene magicamente salvato nell'aggiornamento della scheda. Puoi scoprirlo da solo .

Tutto ciò significa che in IE <= 8 il while (lastTimeoutId--)ciclo può essere eseguito più di 8 cifre e mostrare il messaggio " Uno script in questa pagina sta causando il rallentamento di Internet Explorer ". Quindi, se non è possibile salvare tutti gli ID timeout o non si desidera sovrascrivere window.setTimeout, è possibile considerare di salvare il primo ID timeout su una pagina e cancellare i timeout fino a quando non viene visualizzato.

Esegui il codice al caricamento iniziale della pagina:

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 poi cancella tutti i timeout in sospeso che probabilmente sono stati impostati da codice esterno tante volte che vuoi


più uno per chiarire alcune informazioni più dettagliate.
Sanjay

8

Che ne dici di memorizzare gli id ​​di timeout in un array globale e definire un metodo per delegare la chiamata alla funzione alla finestra.

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
    }
};

3

Devi riscrivere il window.setTimeoutmetodo e salvare il suo ID di timeout.

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();
  }
}

3

Per cancellare tutti i timeout essi devono essere "catturati" primo :

Posiziona il codice seguente prima di qualsiasi altro script e creerà una funzione wrapper per l'originale setTimeout& clearTimeout.

Un nuovo clearTimeoutsmetodo verrà aggiunto windowall'Oggetto, che consentirà di cancellare tutti i timeout (in sospeso) ( collegamento Gist ).

Altre risposte non supportano completamente i possibili argomenti che setTimeout potrebbero ricevere.

// 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);

2

Per completezza, volevo pubblicare una soluzione generale che copra sia setTimeoute setInterval.

Sembra che i browser potrebbero utilizzare lo stesso pool di ID per entrambi, ma da alcune delle risposte a ClearTimeout e clearInterval sono uguali? , non è chiaro se sia sicuro fare affidamento clearTimeoute clearIntervalsvolgere la stessa funzione o lavorare solo sui rispettivi tipi di timer.

Pertanto, quando l'obiettivo è eliminare tutti i timeout e gli intervalli, ecco un'implementazione che potrebbe essere leggermente più difensiva tra le implementazioni quando non è in grado di testarli tutti:

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(){}
}

Puoi usarlo per cancellare tutti i timer nella finestra corrente:

clearAll(window);

Oppure puoi usarlo per cancellare tutti i timer in un iframe:

clearAll(document.querySelector("iframe").contentWindow);

tieni presente che questo approccio distrugge il watcher del servizio vue-cli, quindi non puoi eseguire il ricaricamento a caldo durante la pulizia di tutti i timeout e gli intervalli di tempo. Presumo sia lo stesso per altri osservatori di ricarica a caldo per framework come (angolare, reagire, brace ecc.)
Michail Michailidis

1

Uso Vue con 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
        }
    }

0

Usa un timeout globale da cui derivano tutte le altre funzioni. Questo renderà tutto più veloce e sarà più facile da gestire, anche se aggiungerà un po 'di astrazione al tuo codice.


Non ti capisco completamente. Intendi estendere il comportamento della set_timeoutfunzione globale? Potresti fornire un codice di esempio?
marcio

1
Volevo solo dire che hai un timeout globale in esecuzione una volta ogni 50 ms. Ogni altra funzione che richiederebbe un elemento di temporizzazione la prenderebbe quindi dal timeout globale. Google è passato a questo per efficienza, anche se non riesco più a trovare l'articolo che lo cita.
Travis J

0

Abbiamo appena pubblicato un pacchetto che risolve esattamente questo problema.

npm install time-events-manager

Con ciò, puoi visualizzare tutti i timeout e gli intervalli tramite gli oggetti timeoutCollection& intervalCollection. C'è anche una removeAllfunzione che cancella tutti i timeout / intervalli sia dalla raccolta che dal browser.


0

È molto tardi ... ma:

Fondamentalmente, gli ID setTimeout / setInterval vanno in ordine consecutivo, quindi crea una funzione di timeout fittizia per ottenere l'ID più alto, quindi cancella l'intervallo su tutti gli ID inferiori a quello.

const highestId = window.setTimeout(() => {
  for (let i = highestId; i >= 0; i--) {
    window.clearInterval(i);
  }
}, 0);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.