javascript: Очистить все таймауты?

101

Есть ли способ очистить все тайм-ауты из данного окна? Я полагаю, что таймауты хранятся где-то в windowобъекте, но не могу это подтвердить.

Приветствуется любое кроссбраузерное решение.

Марсио
источник
1
возможный дубликат Как остановить все тайм-ауты и интервалы с помощью javascript?
Бернхард Хофманн
6
Чувак, этот вопрос был задан 3 года назад, и на него есть отличные ответы. Не нужно с этим связываться.
Марсио
Я искал это и нашел и то, и другое. Ваш был старше, поэтому я подумал, что было бы неплохо свести два вопроса к одному. У меня всего один голос; еще несколько человек должны будут проголосовать за закрытие, и дублирующаяся ссылка может помочь другим.
Бернхард Хофманн
2
@Bernard Hofmann: Я не уверен, можно ли считать это повторяющимся вопросом, если на этот вопрос был получен ответ. Принятый ответ на этот вопрос от 2010 года был «Нет».
RoboticRenaissance

Ответы:

149

Их нет в объекте окна, но у них есть идентификаторы, которые (afaik) являются последовательными целыми числами.

Таким образом, вы можете очистить все таймауты следующим образом:

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

while (id--) {
    window.clearTimeout(id); // will do nothing if no timeout with id is present
}
user123444555621
источник
1
Это часть спецификации или может зависеть от реализации?
Тихон Джелвис
7
Он не обязательно должен начинаться с 1. В спецификации HTML5 говорится: «Пусть handle будет определяемым пользовательским агентом целым числом, которое больше нуля, которое будет определять тайм-аут, который будет установлен этим вызовом». что оставляет место для дескриптора, которое может быть любым положительным целым числом, включая непоследовательные и немалые целые числа.
Майк Сэмюэл
3
Это очень умно, но OP, вероятно, должен спросить, есть ли лучшее решение, которое отслеживает активные таймеры.
Джейсон Харвиг
3
Разве это не бесконечный цикл? Не все браузеры отображают if (0) как false.
Travis J
2
Поскольку я спросил, как очистить ВСЕ таймауты, я принимаю этот ответ. Очень умное решение.
Marcio
79

Я думаю, что самый простой способ добиться этого - хранить все setTimeoutидентификаторы в одном массиве, где вы можете легко выполнять итерацию clearTimeout()по всем из них.

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
У этого есть бонус (или потенциальный недостаток, я думаю) в том, что очищаются только те, которые вы установили, поэтому вы случайно не сломаете внешний код.
Тихон Джелвис
1
+1, не очистит все тайм-ауты, но это хорошее решение, чтобы очистить определенную группу таймаутов сразу
Марсио
1
Это лучшее решение, потому что оно не нарушит внешний код, но, поскольку я спросил, как очистить все таймауты, я в итоге принял ответ @ Pumbaa80 :)
marcio
2
@marcioAlmada Странно, но здорово, что вы вернетесь, чтобы прокомментировать это снова через 2,5 года :)
Майкл Берковски
6
Возможно, я хотел бы очистить все таймауты и интервалы, когда я захожу на веб-страницу, которую я не контролирую, потому что у них есть раздражающая всплывающая реклама, которая не запускается, пока не запустится мой блокировщик рекламы.
RoboticRenaissance
12

У меня есть дополнение к ответу Pumbaa80, которое может быть полезно для тех, кто разрабатывает старые IE.

Да, все основные браузеры реализуют идентификаторы тайм-аута как последовательные целые числа (что не требуется по спецификации ). Хотя начальный номер отличается от браузера к браузеру. Кажется, что Opera, Safari, Chrome и IE> 8 запускают идентификаторы тайм-аута с 1, браузеры на основе Gecko с 2 и IE <= 8 с некоторого случайного числа, которое волшебным образом сохраняется при обновлении вкладки. Вы можете узнать это сами .

Все это означает, что в IE <= 8 while (lastTimeoutId--)цикл может выполняться более 8 раз и показывать сообщение « Сценарий на этой странице вызывает медленную работу Internet Explorer ». Поэтому, если вы не можете сохранить все идентификаторы тайм-аута или не хотите переопределять window.setTimeout, вы можете подумать о сохранении первого идентификатора тайм-аута на странице и очистке тайм-аутов до него.

Выполнить код при ранней загрузке страницы:

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

А затем очистите все ожидающие таймауты, которые, вероятно, были установлены иностранным кодом столько раз, сколько захотите.

Коля Ай
источник
плюс один для уточнения более подробной информации.
Санджай,
8

Как насчет сохранения идентификаторов тайм-аутов в глобальном массиве и определения метода для делегирования вызова функции окну.

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

Вам нужно переписать window.setTimeoutметод и сохранить его идентификатор тайм-аута.

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

Чтобы очистить все таймауты, они должны быть сначала «захвачены» :

Поместите приведенный ниже код перед любым другим скриптом, и он создаст функцию-оболочку для исходного setTimeout& clearTimeout.

clearTimeoutsК windowобъекту будут добавлены новые методы , которые позволят очистить все (ожидающие) таймауты ( ссылка Gist ).

В других ответах отсутствует полная поддержка возможных аргументов 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
источник
2

Для полноты картины я хотел опубликовать общее решение, охватывающее как setTimeoutи setInterval.

Кажется, браузеры могут использовать один и тот же пул идентификаторов для обоих, но из некоторых ответов на вопрос: Являются ли clearTimeout и clearInterval одинаковыми? , неясно, безопасно ли полагаться clearTimeoutи clearIntervalвыполнять одну и ту же функцию или работать только с соответствующими типами таймеров.

Поэтому, когда цель состоит в том, чтобы уничтожить все таймауты и интервалы, вот реализация, которая может быть немного более защитной в разных реализациях, когда не может проверить их все:

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

Вы можете использовать его для очистки всех таймеров в текущем окне:

clearAll(window);

Или вы можете использовать его для очистки всех таймеров в iframe:

clearAll(document.querySelector("iframe").contentWindow);
Крис Кало
источник
имейте в виду, что этот подход уничтожает vue-cli-service watcher, поэтому вы не можете выполнять горячую перезагрузку при очистке всех тайм-аутов и временных интервалов. Я предполагаю, что то же самое и для других наблюдателей за горячей перезагрузкой для таких фреймворков, как (angular, react, ember и т. Д.)
Михаил Михайлидис
1

Я использую Vue с 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
        }
    }
ЛуаньЛуаньЛуань
источник
1

Уже очень поздно ... но:

По сути, идентификаторы setTimeout / setInterval идут в последовательном порядке, поэтому просто создайте фиктивную функцию тайм-аута, чтобы получить наивысший идентификатор, а затем очистите интервал для всех идентификаторов ниже этого.

const highestId = window.setTimeout(() => {
  for (let i = highestId; i >= 0; i--) {
    window.clearInterval(i);
  }
}, 0);
Чарри
источник
Это было для меня лучшим решением !! спасибо, Чарри
Locos
0

Используйте глобальный тайм-аут, от которого зависят все ваши другие функции. Это заставит все работать быстрее и упростит управление, хотя добавит некоторую абстракцию в ваш код.

Трэвис Дж.
источник
Я вас не совсем понимаю. Вы хотите расширить поведение set_timeoutглобальной функции? Не могли бы вы привести пример кода?
Marcio
1
Я просто имел в виду, что у вас есть один глобальный тайм-аут, который запускается каждые 50 мс. Любая другая функция, которой потребуется элемент времени, затем получит его из глобального тайм-аута. Google переключился на это для повышения эффективности, хотя я больше не могу найти статью, в которой это цитируется.
Travis J
0

Мы только что опубликовали пакет, решающий именно эту проблему.

npm install time-events-manager

При этом вы можете просматривать все таймауты и интервалы с помощью timeoutCollection& intervalCollectionобъектов. Также есть removeAllфункция, которая очищает все таймауты / интервалы как из коллекции, так и из браузера.

Бар Голдинфельд
источник