IE8 setInterval и setTimeout запускаются сразу после 49 дней безотказной работы - PullRequest
10 голосов
/ 04 декабря 2010

Поскольку время работы системы Windows приближается к 49,7 дням, внутренний счетчик тиков в миллисекундах Windows приближается к 2 ^ 32.Ошибка в Internet Explorer 8, кажется, имеет арифметическое переполнение при вычислении, когда вызывать событие setInterval или setTimeout.Например, если вы работаете на 49-й день и звоните по телефону

setInterval(func, 86400000); // fire event in 24 hours

, функция будет вызвана немедленно, а не через 24 часа.

Эта ошибка, вероятно, возникнет в любое время после25 дней безотказной работы (2 ^ 31 миллисекунды), если достаточно большое число передается в setInterval или setTimeout.(Однако я проверил только на 49 день.)

Вы можете проверить количество дней безотказной работы, введя в командной строке «net statistics server».

Есть ли обходной путь?

Ответы [ 3 ]

5 голосов
/ 04 декабря 2010

Вы можете обойти ошибку, используя оболочку для setTimeout

function setSafeTimeout(func, delay){
    var target = +new Date + delay;
    return setTimeout(function(){
        var now = +new Date;
        if(now < target) {
            setSafeTimeout(func, target - now);
        } else {
            func();
        }
    }, delay);
}

Это все еще возвращает значение от setTimeout, поэтому, если ошибка не обнаружена, clearTimeout все еще может быть использовано. Если clearTimeout должен быть пуленепробиваемым или вам нужно setInterval (и, вероятно, clearInterval), вам нужно добавить больше кода для решения проблемы, но принцип проверки достаточного времени истек до выполнения func. *

3 голосов
/ 07 января 2011

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

Эта проблема также затрагивает IE 7 и, похоже, также влияет на IE 6, но с худшими последствиями, поскольку браузер перестает отвечать всем вместе, и решения, похоже, не работают и в этой версии. Все еще есть много пользователей этих старых версий, особенно в мире бизнеса / предпринимательства.

Я не обнаружил, что левая кнопка мыши была фактором в Windows XP, проблема возникает без него.

Первые два ответа хороши, если задержка тайм-аута составляет самое большее несколько секунд и в приложении установлено очень небольшое количество тайм-аутов. Если требуется все больше и больше тайм-аутов, нужно сделать еще больше, чтобы веб-приложение перестало работать. В RIA Web 2.0 с использованием инфраструктуры, такой как qooxdoo , пользователи могут оставлять ее включенной на несколько дней, поэтому может возникнуть большая потребность в большем и большем времени ожидания, чем несколько полсекундных задержек для создания анимации или другого краткого эффекта.

Первое решение - хорошее начало, но установка следующего тайм-аута на target-now снова вызовет немедленный вызов функции, потому что это все равно сделает время безотказной работы + задержку более 2 ^ 32 миллисекунд, и, следовательно, код JS будет вращаться до тех пор, пока время безотказной работы приближается к 0 (или пользователь убивает браузер).

Второе решение является улучшением, потому что преждевременные тайм-ауты будут происходить только с интервалами в 1 секунду до тех пор, пока сейчас не будет в течение 1 секунды после циклического изменения времени работы, что позволяет другому коду заглядывать, но эксперименты показали этого может быть достаточно, чтобы сделать браузер непригодным для использования, если в игре достаточно ожидающих таймаутов. И это будет продолжаться до тех пор, пока время работы не будет сокращено, поэтому, если запрошенная задержка достаточно велика, пользователь все равно может решить закрыть браузер.

Менее ресурсоемким решением является установка задержки для каждого последующего тайм-аута равной половине продолжительности предыдущей задержки до тех пор, пока эта задержка не станет меньше 500 мс, и тогда мы узнаем, что точка перехода в рабочее состояние неизбежна (<1 второй раз), и мы можем установить следующий тайм-аут на <code>target-now, чтобы преждевременная проверка тайм-аута прекращалась только после небольшого числа последующих циклов. Сколько времени потребуется, чтобы достичь этого, будет зависеть от того, как долго была первоначальная задержка, и насколько близко было время перехода в рабочее время при вызове setSafeTimeout, но в конечном итоге и при минимальной загрузке ЦП приложение возвращается к нормальному поведению без какого-либо длительного замедления пользователя .

Примерно так:

function setSafeTimeout(func, delay) {
  var target = +new Date + delay;
  var newDelay = delay;
  var helper = function()
  {
    var now = +new Date;
    if (now < target)
    {
      newDelay /= 2; // halve the wait time and try again
      if(newDelay < 500) // uptime wrap around is imminent
      {
        newDelay = target-now; // go back to using original target
      }
      var handle = setTimeout(helper, newDelay);
      // if required record handle somewhere for clearTimeout
    }
    else
    {
      func();
    }
  };
  return setTimeout(helper, delay);
};

Уточнение:

Я обнаружил, что setTimeout() может иногда вызывать обратный вызов на несколько миллисекунд раньше, чем ожидалось, даже если время работы системы не близко к 2 ^ 32 мс. В этом случае следующий интервал ожидания, используемый в вышеуказанной функции, может быть больше, чем время, оставшееся до первоначальной цели, что приводит к более длительному ожиданию, чем первоначально требовалось.

Ниже приведена другая версия, которая решает эту проблему:

function setSafeTimeout(func, delay) {
  var target = +new Date + delay;
  var newDelay = delay;
  var helper = function()
  {
    var now = +new Date;
    if (now < target)
    {
      var timeToTarget = target-now;
      newDelay /= 2; // halve the wait time and try again
      if(newDelay < 500 || newDelay > timeToTarget) // uptime wrap around is imminent
      {
        newDelay = timeToTarget; // go back to using original target
      }
      var handle = setTimeout(helper, newDelay);
      // if required record handle somewhere for clearTimeout
    }
    else
    {
      func();
    }
  };
  return setTimeout(helper, delay);
};
1 голос
/ 07 декабря 2010

Вариант ответа Кэмерона Джордана:

function setSafeTimeout(func, delay) {
    var target = +new Date + delay;
    var helper = function() {
        var now = +new Date;
            if (now < target) {
                setTimeout(arguments.callee, 1000);
            } else {
                func();
            }
        }
    return setTimeout(helper, delay);
}

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

Полезная утилита для AdjustTickCount (только для Windows XP).Например, установка счетчика «Новый тик» на 0xffff0000 даст вам 65 секунд с ошибками, прежде чем счетчик тиков перевернется.Любой таймер, установленный, скажем, на 120 секунд, не будет срабатывать должным образом.

Кроме того, в Windows XP ошибочное поведение setTimeout казалось связанным с нажатой левой кнопкой мыши, согласно этой записи .

...