Очень быстрый setTimeout


Какой минимальный таймаут можно указать функции setTimeout? Указать можно одну миллисекунду, но вот реально минимальная задержка в разных браузерах будет разная, но больше миллисекунды, иногда значительно. Создадим небольшой тест. var i = 0, d = new Date(), N = 1000; (function() { if (i < N) { i++; setTimeout(arguments.callee, 1); } else { alert((new Date() - d) / N); } })(); Этот код делает N последовательных (не рекурсивных) вызовов функции через setTimeout и выводит реальную среднюю задержку в миллисекундах. В идеале она должна быть равна 1, на практике всё гораздо печальней: в Firefox и Safari минимальная задержка в среднем равна 11 мс, в Opera — 2 мс, Chrome — 4 мс, IE — 15 мс. Тем не менее есть возможность написать setTimeout с "нулевой" задержкой. Во всех современных браузерах, кроме IE младше 8-й версии, у объекта window есть метод postMessage, который отправляет окну/фрейму текстовое сообщение. Задержка между отправкой сообщения и его получением минимальная, чем и можно воспользоваться. Во всех браузерах, кроме IE, функция, как нам и требуется, асинхронная, т.е. отправляется сообщение в одном потоке выполнения, а доходит до получателя уже в другом. В IE же postMessage работает синхронно, так что отправка сообщения в нём равносильна прямому вызову функции. Поэтому в IE придётся довольствоваться медленным setTimeout. Для остальных браузеров код будет такой. var setZeroTimeout = (function() { var fn, ctx; window.addEventListener('message', function() { if (fn) { fn.call(ctx); } }, false); return function(_fn, _ctx) { fn = _fn; ctx = _ctx; window.postMessage('', '*'); }; })(); Если вы на своём сайте используете postMessage для других целей, можно создать скрытый iframe и посылать сообщения в него. Перепишем тест под использование setZeroTimeout. var i = 0, d = new Date(), N = 1000; (function() { if (i < N) { i++; setZeroTimeout(arguments.callee); } else { alert((new Date() - d) / N); } })(); Результаты будут примерно следующие: в Safari 2 мс, в Opera 0.04 мс (т.е. доли миллисекунды). В остальных браузерах результаты разнятся в зависимости от N: чем больше N, тем меньше среднее время. В Firefox — от 10 до 0.07 мс, в Chrome — от 1 до 0.02 мс. Как видите, результат налицо — реализованный setZeroTimeout гораздо быстрее нативного setTimeout.