Использовать ли setInterval


Чем плох setInterval Для многократного запуска кода через равные промежутки времени предназначена функция setInterval. Тем не менее она обладает рядом минусов, в основном это разное поведение в различных браузерах. Первое отличие состоит в разности момента установки таймера для следующего запуска. Создадим небольшой тест: будем отмерять количество времени, прошедшее с начала выполнения предыдущего запуска и с его окончания. var d1 = new Date(), d2 = new Date(); setInterval(function() { var d = new Date(); document.body.innerHTML += (d - d1) + ' ' + (d - d2) + '<br>'; // Ставим метку в начале функции d1 = new Date(); while (new Date() - d1 < 200); // ничего не делаем 200 миллисекунд // И в конце функции d2 = new Date(); }, 1000); Вывод будет информативным, начиная со второй строчки. В Firefox, Opera, Safari и Chrome ситуация будет схожая: первое число будет примерно равно 1000, второе — на 200 меньше. Различие будут только в разбросе значений. Самый маленький разброс в Chrome и Opera. 1000 800 1003 803 1004 804 1018 808 987 787 1003 803 1000 800 Чуть больше разброс в Safari. 1035 835 1259 1059 841 641 1091 891 1048 848 1005 805 998 798 И самый большой — в Firefox. 988 788 985 785 1007 807 971 771 984 784 990 790 974 774 1010 810 1004 804 В Internet Explorer ситуация более стабильная, но противоположная: 1000 равно второе число, а первое на 200 больше. 1203 1000 1203 1000 1203 1000 1203 1000 1204 1000 1203 1000 1203 1000 Другими словами IE устанавливает таймер на следующий запуск после выполнения callback-функции, а остальные браузеры — до. Поэтому, например, анимация, реализованная с помощью setInterval в Internet Explorer всегда будет медленнее, чем в других браузерах. Ещё одно отличие менее заметное и более трудновоспроизводимое, но иногда способное доставить много хлопот, — это устойчивость к изменению системного времени. Если запустить следующий тест setInterval(function() { document.body.innerHTML = Math.random(); }, 500); И после запуска перевести системное время на минуту назад, то в браузерах Firefox и Safari смена чисел приостановится, а через минуту запустится вновь. Конечно, ручной перевод системного времени крайне редкая ситуация, но на многихсистемах настроена автоматическая синхронизация времени с серверами в интернете, поэтому в некоторых ситуациях нельзя сбрасывать со счетов этот фактор. Ещё один маленький минус функции setInterval — чтобы была возможность остановить её действие, необходимо где-то запоминать её идентификатор, что не всегда удобно. Чем заменить setInterval Чтобы избавиться от перечисленных недостатков setInterval можно использовать многократный setTimeout, для это есть очень удобный паттерн. (function() { // Выполняем периодические действия setTimeout(arguments.callee, 500); })(); Здесь мы создаём безымянную функцию, тут же её вызываем, и в последующем передаём её же функции setTimeout. При этом типичная асинхронная функция, выполняющая какие-то периодические действия, выглядит следующим образом. /** * @param {Function} callback Фукнция, вызываемая по окончании работы. */ function foo(callback) { // Инициализируем переменные (function() { // Выполняем действия if (/* больше ничего делать не надо */) { callback(); } else { setTimeout(arguments.callee, 500); } })(); } Обратите внимание, в отличие от setInterval, в данном случае первая итерация выполняется сразу же, без задержки. Напишем, например, функцию, посимвольно выводящую строку в нужный DOM-элемент, и вызывающую callback-функцию по окончанию работы. function typeString(elId, str, callback) { var i = 1, el = document.getElementById(elId); (function() { el.innerHTML = str.substr(0, i++); if (i > str.length) { callback(); } else { setTimeout(arguments.callee, 300); } })(); } typeString('t', 'Hello, World!', function() { alert('Напечатали'); });