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
.