javascript: Czy usunąć wszystkie limity czasu?

99

Czy istnieje sposób na wyczyszczenie wszystkich limitów czasu z danego okna? Przypuszczam, że limity czasu są przechowywane gdzieś w windowobiekcie, ale nie mogłem tego potwierdzić.

Mile widziane są dowolne rozwiązania dla różnych przeglądarek.

marcio
źródło
6
Koleś, to pytanie jest sprzed 3 lat i zawiera świetne odpowiedzi. Nie musisz się z tym bawić.
marcio
Szukałem tego i znalazłem oba. Twój był starszy, więc pomyślałem, że byłoby dobrze, gdybyś ograniczył te dwa pytania do jednego. Mój to tylko jeden głos; jeszcze kilka osób będzie musiało zagłosować za zamknięciem, a zduplikowany link może pomóc innym.
Bernhard Hofmann
2
@Bernard Hofmann Nie jestem pewien, czy liczy się jako zduplikowane pytanie, jeśli otrzymano lepszą odpowiedź. Zaakceptowaną odpowiedzią na ten z 2010 roku było „Nie”.
RoboticRenaissance

Odpowiedzi:

148

Nie ma ich w obiekcie window, ale mają identyfikatory, które (afaik) są kolejnymi liczbami całkowitymi.

Możesz więc wyczyścić wszystkie limity czasu w następujący sposób:

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

while (id--) {
    window.clearTimeout(id); // will do nothing if no timeout with id is present
}
user123444555621
źródło
1
Czy to część specyfikacji, czy też może być zależna od implementacji?
Tikhon Jelvis,
7
Nie musi zaczynać się od 1. Specyfikacja HTML5 mówi: „Niech uchwyt będzie liczbą całkowitą zdefiniowaną przez klienta, która jest większa od zera, która będzie określać limit czasu ustawiany przez to wywołanie”. co pozostawia miejsce dla uchwytu na dowolną dodatnią liczbę całkowitą, w tym nie kolejne i nie małe liczby całkowite.
Mike Samuel,
3
To bardzo sprytne, ale OP powinien prawdopodobnie zapytać, czy istnieje lepsze rozwiązanie, które śledzi aktywne zegary.
Jason Harwig,
3
Czy to nie jest nieskończona pętla? Nie wszystkie przeglądarki renderują if (0) jako fałsz.
Travis J
2
Ponieważ zapytałem, jak wyczyścić WSZYSTKIE limity czasu, akceptuję tę odpowiedź. Bardzo sprytne rozwiązanie.
marcio
79

Myślę, że najłatwiejszym sposobem na osiągnięcie tego byłoby przechowywanie wszystkich setTimeoutidentyfikatorów w jednej tablicy, w której można łatwo iterować clearTimeout()na nich wszystkich.

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]);
}
Michaela Berkowskiego
źródło
14
Ma to tę premię (lub potencjalne wady, chyba) z tylko czyszcząc te ty ustawione, więc nie przypadkowo złamać kod na zewnątrz.
Tikhon Jelvis
1
+1, nie skasuje wszystkie limity czasu, ale jest to dobre rozwiązanie, aby usunąć grupę specyficzną limity czasu naraz
Marcio
1
To najlepsze rozwiązanie, ponieważ nie złamie zewnętrznego kodu, ale ponieważ zapytałem, jak wyczyścić wszystkie limity czasu, ostatecznie zaakceptowałem odpowiedź @ Pumbaa80 :)
marcio
2
@marcioAlmada Dziwne, ale fajnie widzieć, jak wracasz, aby skomentować to ponownie 2,5 roku później :)
Michael Berkowski
6
Być może chciałbym wyczyścić wszystkie limity czasu i odstępy czasu, gdy wchodzę na stronę internetową, której nie kontroluję, ponieważ mają irytujące wyskakujące reklamy, które pojawiają się dopiero po uruchomieniu mojego adblockera.
RoboticRenaissance
12

Mam dodatek do odpowiedzi Pumba80, który może być przydatny dla kogoś, kto tworzy dla starych IE.

Tak, wszystkie główne przeglądarki implementują identyfikatory limitów czasu jako kolejne liczby całkowite (co nie jest wymagane przez specyfikację ). Chociaż numer początkowy różni się od przeglądarki do przeglądarki. Wygląda na to, że Opera, Safari, Chrome i IE> 8 uruchamia identyfikatory limitów czasu z 1, przeglądarki oparte na Gecko z 2 i IE <= 8 z jakiejś losowej liczby, która jest magicznie zapisywana podczas odświeżania karty. Możesz sam to odkryć .

Wszystko to oznacza, że ​​w IE <= 8 while (lastTimeoutId--)cykl może trwać ponad 8 cyfr razy i wyświetlać komunikat „ Skrypt na tej stronie powoduje powolne działanie przeglądarki Internet Explorer ”. Więc jeśli nie możesz zapisać wszystkich swoich timeout id lub nie chcesz nadpisywać window.setTimeout , możesz rozważyć zapisanie pierwszego timeout id na stronie i wyczyszczenie timeoutów do tego czasu.

Wykonaj kod przy wczesnym ładowaniu strony:

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

A następnie wyczyść wszystkie oczekujące limity czasu, które prawdopodobnie zostały ustawione przez obcy kod tyle razy, ile chcesz

Kolya Ay
źródło
plus jeden za wyjaśnienie bardziej szczegółowych informacji.
Sanjay
8

Co powiesz na przechowywanie identyfikatorów limitów czasu w tablicy globalnej i zdefiniowanie metody delegowania wywołania funkcji do okna.

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
źródło
3

Musisz przepisać window.setTimeoutmetodę i zapisać jej identyfikator limitu czasu.

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();
  }
}
Gość
źródło
3

Aby usunąć wszystkie limity czasu muszą zostać „przechwycone” pierwszy :

Umieść poniższy kod przed jakimkolwiek innym skryptem, a utworzy on funkcję opakowującą dla oryginalnego setTimeout& clearTimeout.

clearTimeoutsDo windowobiektu zostaną dodane nowe metody , które pozwolą na wyczyszczenie wszystkich (oczekujących) limitów czasu ( łącze Gist ).

W innych odpowiedziach brakuje pełnego poparcia dla możliwych argumentów 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
źródło
2

Dla kompletności chciałem zamieścić ogólne rozwiązanie, które obejmuje zarówno setTimeouti setInterval.

Wygląda na to, że przeglądarki mogą używać tej samej puli identyfikatorów w obu przypadkach, ale z niektórych odpowiedzi na pytanie Czy clearTimeout i clearInterval są takie same? , nie jest jasne, czy można bezpiecznie polegać clearTimeouti clearIntervalwykonywać tę samą funkcję, czy też pracować tylko na odpowiednich typach timerów.

Dlatego, gdy celem jest zabicie wszystkich limitów czasu i interwałów, oto implementacja, która może być nieco bardziej defensywna we wszystkich implementacjach, gdy nie można przetestować ich wszystkich:

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

Możesz go użyć do wyczyszczenia wszystkich timerów w bieżącym oknie:

clearAll(window);

Lub możesz go użyć do wyczyszczenia wszystkich timerów w iframe:

clearAll(document.querySelector("iframe").contentWindow);
Chris Calo
źródło
pamiętaj, że takie podejście niszczy obserwatora usługi vue-cli-service, więc nie możesz wykonać ponownego ładowania podczas czyszczenia wszystkich limitów czasu i przedziałów czasowych. Zakładam, że to samo dotyczy innych obserwatorów przeładowania na gorąco dla ram, takich jak (kątowy, reagujący, żar itp.)
Michail Michailidis
1

Używam Vue z Typescriptem.

    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
źródło
0

Użyj globalnego limitu czasu, z którego pochodzą wszystkie inne funkcje. Dzięki temu wszystko będzie działać szybciej i będzie łatwiejsze w zarządzaniu, chociaż doda trochę abstrakcji do twojego kodu.

Travis J.
źródło
Nie do końca cię rozumiem. Chcesz rozszerzyć zachowanie set_timeoutfunkcji globalnej? Czy mógłbyś podać przykładowy kod?
marcio
1
Chodziło mi tylko o to, że masz jeden globalny limit czasu działający raz na 50 ms. Każda inna funkcja, która wymagałaby elementu czasowego, pobierałaby go wtedy z globalnego limitu czasu. Google przeszło na to ze względu na wydajność, chociaż nie mogę już znaleźć cytowanego artykułu.
Travis J
0

Właśnie opublikowaliśmy pakiet rozwiązujący dokładnie ten problem.

npm install time-events-manager

Dzięki temu możesz przeglądać wszystkie limity czasu i interwały za pośrednictwem timeoutCollection& intervalCollectionobiektów. Istnieje również removeAllfunkcja, która usuwa wszystkie limity czasu / interwały zarówno z kolekcji, jak i przeglądarki.

Bar Goldinfeld
źródło
0

Jest już bardzo późno ... ale:

Zasadniczo identyfikatory setTimeout / setInterval idą w kolejności, więc po prostu utwórz fikcyjną funkcję limitu czasu, aby uzyskać najwyższy identyfikator, a następnie wyczyść interwał dla wszystkich identyfikatorów niższych od tego.

const highestId = window.setTimeout(() => {
  for (let i = highestId; i >= 0; i--) {
    window.clearInterval(i);
  }
}, 0);
charri
źródło