Javascript: Alle Timeouts löschen?

99

Gibt es eine Möglichkeit, alle Zeitüberschreitungen in einem bestimmten Fenster zu löschen? Ich nehme an, die Zeitüberschreitungen sind irgendwo im windowObjekt gespeichert , konnten dies aber nicht bestätigen.

Jede Cross-Browser-Lösung ist willkommen.

Marcio
quelle
6
Alter, diese Frage stammt von vor 3 Jahren und hat großartige Antworten. Damit muss man sich nicht anlegen.
Marcio
Ich habe danach gesucht und beides gefunden. Ihre war älter, also dachte ich, es wäre eine gute Haushaltsführung, die beiden Fragen auf eine zu reduzieren. Meins ist nur eine Stimme; Einige weitere Personen müssen abstimmen, um zu schließen, und der doppelte Link kann anderen helfen.
Bernhard Hofmann
2
@Bernard Hofmann Ich bin mir nicht sicher, ob es als doppelte Frage gilt, wenn diese eine bessere Antwort bekommen hat. Die akzeptierte Antwort auf diese Frage aus dem Jahr 2010 war "Nein".
RoboticRenaissance

Antworten:

148

Sie befinden sich nicht im Fensterobjekt, aber sie haben IDs, die (afaik) aufeinanderfolgende ganze Zahlen sind.

So können Sie alle Zeitüberschreitungen wie folgt löschen:

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

while (id--) {
    window.clearTimeout(id); // will do nothing if no timeout with id is present
}
user123444555621
quelle
1
Ist dieser Teil der Spezifikation oder könnte er implementierungsabhängig sein?
Tikhon Jelvis
7
Es muss nicht bei 1 beginnen. In der HTML5- Spezifikation heißt es: "Handle sei eine vom Benutzeragenten definierte Ganzzahl, die größer als Null ist und das durch diesen Aufruf festzulegende Zeitlimit identifiziert." Dies lässt Platz für das Handle, um eine positive ganze Zahl zu sein, einschließlich nicht aufeinanderfolgender und nicht kleiner ganzer Zahlen.
Mike Samuel
3
Das ist sehr klug, aber das OP sollte wahrscheinlich fragen, ob es eine bessere Lösung gibt, die aktive Timer verfolgt.
Jason Harwig
3
Ist das nicht eine Endlosschleife? Nicht alle Browser rendern if (0) als false.
Travis J
2
Da ich gefragt habe, wie ALLE Zeitüberschreitungen behoben werden sollen, akzeptiere ich diese Antwort. Sehr clevere Lösung.
Marcio
79

Ich denke, der einfachste Weg, dies zu erreichen, besteht darin, alle setTimeoutBezeichner in einem Array zu speichern , zu dem Sie clearTimeout()auf allen leicht iterieren können.

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
quelle
14
Dies hat den Bonus (oder einen möglichen Nachteil, denke ich), nur die von Ihnen festgelegten zu löschen , damit Sie nicht versehentlich außerhalb des Codes brechen.
Tikhon Jelvis
1
+1, löscht nicht alle Zeitüberschreitungen, ist aber eine gute Lösung, um eine bestimmte Gruppe von Zeitüberschreitungen auf einmal zu löschen
Marcio
1
Dies ist die beste Lösung, da dadurch kein externer Code beschädigt wird. Da ich jedoch gefragt habe, wie alle Zeitüberschreitungen behoben werden sollen, habe ich die Antwort von @ Pumbaa80 akzeptiert :)
marcio
2
@marcioAlmada Seltsam, aber cool zu sehen, dass Sie zurückkehren, um dies 2,5 Jahre später wieder zu kommentieren :)
Michael Berkowski
6
Vielleicht möchte ich alle Zeitüberschreitungen und Intervalle löschen, wenn ich eine Webseite betrete, die ich nicht kontrolliere, weil sie eine nervige Popup-Anzeige haben, die erst startet, nachdem mein Adblocker ausgeführt wird.
RoboticRenaissance
12

Ich habe eine Ergänzung zu Pumbaa80s Antwort , die für jemanden nützlich sein könnte, der sich für alte IEs entwickelt.

Ja, alle gängigen Browser implementieren Timeout-IDs als aufeinanderfolgende Ganzzahlen (was in der Spezifikation nicht erforderlich ist ). Obwohl die Startnummer von Browser zu Browser unterschiedlich ist. Es scheint, dass Opera, Safari, Chrome und IE> 8 Timeout-IDs von 1, Gecko-basierte Browser von 2 und IE <= 8 von einer Zufallszahl aus starten, die bei der Aktualisierung von Registerkarten auf magische Weise gespeichert wird. Sie können es selbst entdecken .

Alles, was bedeutet, dass der while (lastTimeoutId--)Zyklus in IE <= 8 mehr als 8 -stellig ausgeführt werden kann und die Meldung " Ein Skript auf dieser Seite bewirkt, dass Internet Explorer langsam ausgeführt wird " angezeigt wird. Wenn Sie also nicht alle Timeout- IDs speichern können oder window.setTimeout nicht überschreiben möchten , können Sie die erste Timeout-ID auf einer Seite speichern und Timeouts bis dahin löschen .

Führen Sie den Code beim frühen Laden der Seite aus:

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

Und löschen Sie dann alle ausstehenden Zeitüberschreitungen, die wahrscheinlich so oft durch Fremdcode festgelegt wurden, wie Sie möchten

Kolya Ay
quelle
plus eins zur Verdeutlichung einiger detaillierterer Informationen.
Sanjay
8

Wie wäre es, wenn Sie die Timeout-IDs in einem globalen Array speichern und eine Methode definieren, um den Funktionsaufruf an die Fenster zu delegieren.

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
quelle
3

Sie müssen die window.setTimeoutMethode neu schreiben und ihre Timeout-ID speichern.

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

Um alle zu löschen Timeouts sie müssen „eingefangen“ werden zuerst :

Platzieren Sie den folgenden Code vor jedem anderen Skript und es wird eine Wrapper-Funktion für das Original setTimeout& erstellt clearTimeout.

Dem Objekt werden neue clearTimeoutsMethoden hinzugefügt window, mit denen alle (ausstehenden) Zeitüberschreitungen gelöscht werden können ( Link Link ).

Andere Antworten haben keine vollständige Unterstützung für mögliche Argumente setTimeout .

// 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
quelle
2

Der Vollständigkeit halber wollte ich eine allgemeine Lösung veröffentlichen, die sowohl setTimeoutals auch abdeckt setInterval.

Es scheint, dass Browser für beide denselben ID-Pool verwenden, aber aus einigen Antworten auf Sind clearTimeout und clearInterval gleich? Es ist nicht klar, ob es sicher ist, sich auf dieselbe Funktion zu verlassen clearTimeoutund clearIntervalsie auszuführen oder nur an ihren jeweiligen Timertypen zu arbeiten.

Wenn das Ziel darin besteht, alle Zeitüberschreitungen und Intervalle zu beenden, finden Sie hier eine Implementierung, die bei Implementierungen möglicherweise etwas defensiver ist, wenn nicht alle getestet werden können:

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

Sie können damit alle Timer im aktuellen Fenster löschen:

clearAll(window);

Oder Sie können es verwenden, um alle Timer in einem iframe:

clearAll(document.querySelector("iframe").contentWindow);
Chris Calo
quelle
Beachten Sie, dass dieser Ansatz den Vue-Cli-Service-Watcher zerstört, sodass Sie beim Reinigen aller Zeitüberschreitungen und Zeitintervalle kein Hot-Reload durchführen können. Ich gehe davon aus, dass dies auch für andere Hot-Reload-Beobachter für Frameworks wie (eckig, reagieren, glühen usw.) gilt
Michail Michailidis
1

Ich benutze Vue mit 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
quelle
0

Verwenden Sie ein globales Timeout, von dem alle anderen Funktionen das Timing ableiten. Dadurch wird alles schneller ausgeführt und einfacher zu verwalten, obwohl Ihr Code dadurch etwas abstrahiert wird.

Travis J.
quelle
Ich verstehe dich nicht ganz. Sie wollen das Verhalten der set_timeoutglobalen Funktion erweitern? Könnten Sie einen Beispielcode geben?
Marcio
1
Ich habe nur gemeint, dass Sie alle 50 ms eine globale Zeitüberschreitung haben. Jede andere Funktion, die ein Timing-Element erfordern würde, würde es dann aus dem globalen Timeout herausholen. Google hat aus Effizienzgründen darauf umgestellt, obwohl ich den Artikel, in dem es zitiert wird, nicht mehr finden kann.
Travis J
0

Wir haben gerade ein Paket veröffentlicht, das genau dieses Problem löst.

npm install time-events-manager

Damit können Sie alle Timeouts und Intervalle über timeoutCollection& intervalCollectionObjekte anzeigen . Es gibt auch eine removeAllFunktion, die alle Zeitüberschreitungen / Intervalle sowohl aus der Sammlung als auch aus dem Browser löscht.

Bar Goldinfeld
quelle