2014-01-13 3 views
29

Я получил этот код здесь:В чем причина JavaScript setTimeout настолько неточна?

var date = new Date(); 
setTimeout(function(e) { 
    var currentDate = new Date(); 
    if(currentDate - date >= 1000) { 
     console.log(currentDate, date); 
     console.log(currentDate-date); 
    } 
    else { 
     console.log("It was less than a second!"); 
     console.log(currentDate-date); 
    } 
}, 1000); 

В моем компьютере, он всегда выполняет правильно, с 1000 в консоли вывода. Заинтересовано в другом компьютере, тот же самый код, тайм-аут обратный вызов начинается менее чем за второй и разность currentDate - date между 980 и 998.

Я знаю о существовании библиотек, которые решают эту неточность (например, Tock).

В основном, мой вопрос: Каковы причины, по которым setTimeout не срабатывает при данной задержке? Может быть, компьютер слишком медленный, и браузер автоматически пытается адаптироваться к медлительности и запускает событие раньше?

PS: Вот скриншот кода и результаты выполняются в консоли Chrome JavaScript:

Enter image description here

+0

Разница между 1-2 миллисекундами (более 1000 миллисекунд), вероятно, связана с джиттером. Почему думаете, что это неточно? Это 1/5 процента. –

+7

John Resig написал хорошую статью об этом :) http://ejohn.org/blog/accuracy-of-javascript-time/ – rorypicko

+0

@ElliottFrisch Я думаю, что это неточно, потому что если я скажу «1 секунду», я хочу просто и точно "1 секунда". Не больше, не меньше. –

ответ

20

Это не должно быть особенно точным. Существует ряд факторов, ограничивающих, как скоро браузер сможет выполнить код; цитирует MDN:

В дополнении к «зажиму», таймаут может также стрелять позже, когда страница (или сама ОС/браузер) занята другими задачами.

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

Однако различные браузеры могут реализовывать его по-разному. Вот некоторые тесты, которые я сделал:

var date = new Date(); 
setTimeout(function(e) { 
    var currentDate = new Date(); 
    console.log(currentDate-date); 
}, 1000); 

// Browser Test1 Test2 Test3 Test4 
// Chrome 998 1014 998 998 
// Firefox 1000 1001 1047 1000 
// IE 11 1006 1013 1007 1005 

Возможно < 1000 раз из Chrome может быть связано с неточностью в Date типа, или, возможно, это может быть, что Chrome использует различные стратегии для принятия решения, когда для выполнения кода — возможно, он пытается поместить его в ближайший временной интервал, даже если задержка ожидания еще не завершена.

Короче говоря, вы не должны использовать setTimeout, если вы ожидаете надежного, последовательного, миллисекундного времени.

+2

Настоящий и важный, но не объясняет, почему ОП измерял тайм-ауты * короче *, чем требовалось. – AmeliaBR

+0

Он был отредактирован, теперь он ... – gbmhunter

7

Кто-то, пожалуйста, поправьте меня, если я искажая эту информацию:

В соответствии с post from John Resig относительно неточности тестов производительности на разных платформах (ударный ток)

С системными временами c по существу округляя до последнего запрошенного времени (каждый около 15   мсек.) качество результатов работы серьезно скомпрометировано.

Так есть до в 15 мс   Фадж на обоих концах, когда по сравнению с системным временем.

+2

Это один из аспектов проблемы. Другой проблемой является асинхронный характер браузера, поэтому в зависимости от того, что еще находится в очереди, таймаут может быть приостановлен для работы с другими заданиями. – EmptyArsenal

16

В общем, компьютерные программы очень ненадежны при попытке выполнить вещи с более высокой точностью, чем 50   мс. Причиной этого является то, что даже на октакоре гиперпоточном процессоре ОС обычно жонглирует несколькими сотнями процессов и потоков, иногда тысяч или более. ОС делает все, что многозадачность работает, планируя все из них, чтобы получить кусочек времени процессора один за другим, а это означает, что им нужно «всего несколько миллисекунд времени, чтобы сделать что-то свое».

Это означает, что если вы установите тайм-аут для 1000   мс, вероятность того, что текущий процесс браузера не будет работать в данный момент времени, невелика, так что браузер совершенно не замечает до 1005, 1010 или даже 1050 миллисекунд, чтобы он выполнял данный обратный вызов.

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

Что касается того, почему тест дает 980, я не уверен, что будет зависеть от того, какой браузер вы используете и какой движок JavaScript. Тем не менее, я могу полностью понять, как браузер просто вручную исправляет бит вниз для загрузки и/или скорости системы, гарантируя, что «в среднем задержка по-прежнему находится в правильном времени» - это будет иметь большое значение из принципа песочницы, чтобы просто приблизительное количество требуемого времени без потенциального обременения остальной части системы.

+0

Действительно, проблема не специфична для JavaScript. И точно 50 мс или менее для браузера, выполняющего интерпретируемый язык, я думаю, что мы уже довольно хорошо прокляты. –

Смежные вопросы