Почему множественные вызовы setTimeout() вызывают такое сильное отставание?

#javascript #firefox #google-chrome #lag

#javascript #firefox #google-chrome #задержка

Вопрос:

У меня есть сложная последовательность анимации, включающая затухания и переходы в JavaScript. Во время этой последовательности, которая состоит из четырех элементов, изменяющихся одновременно, для каждого элемента используется setTimeout .

При тестировании в Internet Explorer 9 анимация работает со скоростью реального времени (она должна была занять 1,6 секунды, а заняла ровно 1,6 секунды). ЛЮБОЙ другой браузер будет ужасно запаздывать со временем анимации 4 секунды (Firefox 3 и 4, Chrome, Opera) и примерно 20 секунд в IE 8 и ниже.

Как IE9 может работать так быстро, в то время как все остальные браузеры застряли в грязи?

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

РЕДАКТИРОВАТЬ: Чтобы уточнить в ответ на комментарии, вот схема кода:

 link.onclick = function() {
    clearTimeout(colourFadeTimeout);
    colourFadeTimeout = setTimeout("colourFade(0);",25);

    clearTimeout(arrowScrollTimeout);
    arrowScrollTimeout = setTimeout("arrowScroll(0);",25);

    clearTimeout(pageFadeOutTimeout);
    pageFadeOutTimeout = setTimeout("pageFadeOut(0);",25);

    clearTimeout(pageFadeInTimeout);
    pageFadeInTimeout = setTimeout("pageFadeIn(0);",25);
}
  

Каждая из четырех функций увеличивает время затухания на один кадр, затем устанавливает другой тайм-аут с увеличенным аргументом до конца анимации.

Вы можете посмотреть страницу по адресу http://adamhaskell.net/cw/index.html (Имя пользователя: knockknock; Пароль: goaway) (у него есть звук и музыка, которые можно отключить, но будьте осторожны!) — мой JavaScript очень неаккуратен, поскольку я на самом деле не организовал его должным образом, но он немного прокомментирован, так что, надеюсь, вы сможете увидеть, в чем заключается общая идея.

Комментарии:

1. Не могли бы вы пояснить? Сама анимация выполняется медленно или существует время ожидания между различными частями анимации из-за ожидания истечения тайм-аута? Также помог бы некоторый код.

2. Четыре части анимации: (1) Исчезновение текущей страницы с помощью opacity , (2) Исчезновение на новой странице с помощью opacity , (3) Исчезновение цвета страницы с помощью background-color , (4) Указатель прокрутки на панели навигации с помощью left (Да, это очень излишне, но это в основном для показухи). Анимация должна выполняться со скоростью 40 кадров в секунду (интервал ожидания составляет 25 мс), но в более медленных браузерах каждый кадр занимает как минимум в два раза больше времени, и видны скачки.

3. Публикация кода может быть для вас проблемой, потому что сейчас он такой грязный…

4. вероятно, вы где-то упускаете танцующего ребенка

5. Я думаю, что происходит слишком много всего одновременно, это не имеет ничего общего с setTimeout . Возможно, вы захотите использовать эту страницу для продвижения улучшенного движка Javascript в IE9, но если вы заинтересованы в том, чтобы другие браузеры могли с этим справиться, вам, возможно, придется немного смягчить эффекты. (Или сделайте их лучшим способом. На самом деле я не собираюсь просматривать ваш беспорядочный скрипт для оптимизации. :))

Ответ №1:

Несколько вещей:

  1. Ваш тайм-аут составляет 25 мс. Это приводит к 40 кадрам в секунду, что является очень высокой частотой кадров, которую можно попытаться достичь с помощью javascript. Особенно для вещей, связанных с манипуляциями с DOM, которые могут вызвать перепрошивки. Увеличьте его до 50 или 60. 15 кадров в секунду должно быть более чем достаточно для тех видов анимации, которые вы делаете. Вы не пытаетесь отображать здесь видео, просто перемещаете объекты по странице.

  2. Не используйте strings в качестве первого параметра для setTimeout() . Особенно если вы заботитесь о производительности. Это заставит javascript перекомпилировать строку в каждом кадре анимации. Вместо этого используйте функцию. Если вам нужно передать аргумент, используйте анонимную функцию для переноса функции, которую вы хотите выполнить:

     setTimeout(function(){
        pageFadeIn(0)
    },50);
      

    это будет скомпилировано только один раз при загрузке скрипта.

  3. Как упоминал Бен, дешевле использовать один setTimeout для планирования функций. Если уж на то пошло, ясность кода может улучшиться, если вместо этого использовать setInterval (а может и нет, зависит от вашего стиля кодирования).


Дополнительный ответ:

Программирование анимации на javascript — это оптимизация и компромисс. На странице можно анимировать множество объектов с небольшим замедлением, но вам нужно знать, как это сделать правильно, и решить, чем пожертвовать. В качестве примера того, как много можно анимировать одновременно, можно привести демо-версию стратегической игры в реальном времени, которую я написал пару лет назад.

Среди вещей, которые я сделал для оптимизации игры, следующие:

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

  2. Я использую один setInterval для всего. Это дешевле с точки зрения процессора и проще в управлении. Просто определите базовую частоту кадров, а затем запланируйте запуск разных анимаций в разное время.

Комментарии:

1. Еще раз спасибо за ответ. Я не знал, что вы можете передать функцию setTimeout — в будущем это намного упростит задачу! Я обязательно дам вам знать, если это сработает. Я также вдвое уменьшу частоту кадров (я позаботился о том, чтобы во всех анимациях было количество кадров в степени два, поскольку это позволяет легко масштабировать и поддерживать идеальную точность чисел, если я не ошибаюсь)

2. Хорошо, все работает нормально. Firefox по-прежнему работает медленнее, но я думаю, что в IE9 лучше просто сам движок, потому что медлительность далеко не так плоха, как была (теперь это занимает едва ли на полсекунды больше, чем следовало бы). Большое вам спасибо за помощь — я не знал, что вы можете передать функцию setTimeout.

3. это очень хороший ответ (и пример)

4. @slebetman Ссылка на ваш ответ больше недоступна. Не могли бы вы перепостить свой исходный код, если он у вас все еще есть?

5. @UpTheCreek: 40 кадров в секунду были очень высокой частотой кадров в 2010 году — это замедлило бы IE6 до обхода. Да, в 2010 году нам все еще приходилось поддерживать IE6 и IE7

Ответ №2:

Что ж, это многовато для javascript (несмотря на «четырехкратную дозу удивительности» 🙂

Вы запускаете много последовательностей setTimeout, я не уверен, насколько хорошо движки JS оптимизированы для этого .. особенно IE <= 8

Хорошо, может быть, просто приблизительное предложение… Возможно, вы могли бы написать небольшой механизм синхронизации.

Поддерживайте глобальный объект, который хранит ваши текущие запущенные временные события с функцией для запуска и задержкой…

Затем создайте один обработчик setTimeout, который проверяет соответствие этому глобальному объекту и уменьшает задержку на каждой итерации, и вызывайте функцию, когда задержка становится < 0

ваше глобальное событие выглядело бы примерно так:

 var events = {

        fade1 : {
            fn : func_name,
            delay : 25,
            params : {}
        }

        fadeArrow : {
            fn : func_name,
            delay : 500,
            params : {}
        }

        slideArrow : {
            fn : func_name,
            delay : 500,
            params : {
                arrow:some_value
            }
        }

    }
  

затем создайте функцию для перебора этих вызовов с интервалом (возможно, 10 или 20 мс) и уменьшите задержки до их завершения и запустите функцию с параметрами в качестве параметра функции (проверьте Function.call для этого).

После завершения работы запустите setTimeout снова с той же задержкой..

Чтобы отменить событие, просто отключите свойство из списка событий..

Создайте несколько методов для добавления / удаления событий в очереди, обновления параметров и так далее..

Это сократило бы все до одного обработчика тайм-аута..

(просто идея)

Комментарии:

1. Я попробую это завтра (поскольку сейчас здесь 3 часа ночи) и дам вам знать, как это получается.