setTimeout и requestAnimationFrame

JavaScript

Что вводить в коде ниже

setTimeout(() => {
    console.log(1);
}, 0)
console.log(2);

Ответ: Выход 2 , 1.

содержание

  • однопоточная модель
  • очередь задач
  • setTimeout
  • setTimeoutа такжеsetInterval
  • requestAnimationFrame
  • requestidlecallback

однопоточная модель

Основная особенность языка JavaScript — однопоточность, то есть одновременно может выполняться только одно действие, предыдущие задачи не завершаются, а последние задачи можно только ждать.

Почему JavaScript однопоточный?

В основном это связано с использованием JavaScript. Его основное использование — взаимодействие с пользователем и управление DOM. Если JavaScript многопоточен, это принесет много сложных проблем. Если JavaScript имеет два потока, A и B, поток A добавляет содержимое в узел DOM, а поток B удаляет узел. Какой из них должен преобладать? Итак, чтобы избегайте сложности, он предназначен для однопоточной обработки.

Хотя HTML5 предлагаетWeb Workerстандарт. Роль Web Worker заключается в создании многопоточной среды для JavaScript, позволяющей основному потоку создавать рабочие потоки и назначать последним некоторые задачи для выполнения. Но дочерний поток полностью контролируется основным потоком и не должен манипулировать DOM. Так что это не меняет сути однопоточного JavaScript. Общий сценарий использования Web Worker заключается в том, что в коде есть много ресурсоемких задач или задач с высокой задержкой, которые можно считать назначенными потокам Worker.

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

JavaScript под браузером

Ядро браузера является многопроцессорным

  • процесс браузера (основной процесс)
    • Отвечает за отображение страницы браузера и взаимодействие с пользователями. например вперед, назад
    • страница вперед, назад
    • Отвечает за управление страницами, создание и уничтожение других процессов.
  • Процесс графического процессора
    • 3D-рендеринг
  • процесс плагина
    • Для каждого типа плагина существует один процесс, который может быть создан только при использовании плагина.
  • Процесс рендеринга в браузере (ядро браузера)
    • Поток рендеринга графического интерфейса
      • Парсинг DOM, парсинг CSS, генерация дерева рендеринга
    • поток движка js
      • выполнить js-код
    • триггер события
      • управляет очередью задач
    • Поток асинхронных HTTP-запросов
    • синхронизированный триггерный поток

Вы можете видеть, что js-движок — это поток процесса рендеринга в браузере.

Связь между потоками в ядре браузера

  • Поток рендеринга GUI и поток движка JS являются взаимоисключающими.
    • js может манипулировать DOM. Если страница отображается во время изменения этих элементов (поток js и поток пользовательского интерфейса выполняются одновременно), данные элемента, полученные до и после потока рендеринга, могут быть несогласованными.
  • JS блокирует загрузку страницы
    • js заблокирует страницу, если ее выполнение займет слишком много времени.

Браузер — это многопроцессорное преимущество

  • По умолчанию открывается новая вкладка и создается новый процесс, поэтому сбой одной вкладки не повлияет на весь браузер.
  • Сбои сторонних плагинов также не влияют на весь браузер.
  • Многопроцессорность позволяет в полной мере использовать преимущества нескольких ядер современных процессоров.
  • Модель песочницы удобно использовать для изоляции таких процессов, как плагины, и повышения стабильности браузера.

Что такое процессы и потоки?

Процесс (process) и поток (thread) — основные понятия операционной системы.

  • Процесс — это наименьшая единица распределения ресурсов ЦП (наименьшая единица, которая может владеть ресурсами и работать независимо).
  • Поток — это наименьшая единица планирования ЦП (единица выполнения программы, основанная на процессе).

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

Если вы хотите узнать больше, я рекомендую книгу "WebKit Technology Insider".

очередь задач

Однопоточность означает, что все задачи должны быть поставлены в очередь на выполнение, а следующая задача будет выполняться после завершения предыдущей задачи.

Если задача должна быть выполнена, но движок JavaScript выполняет другие задачи, то задача должна быть помещена в очередь для ожидания. Когда поток простаивает, вы можете взять самую раннюю добавленную задачу из этой очереди для выполнения (аналогично тому, когда мы идем в банк, чтобы встать в очередь по делу, один поток эквивалентен тому, что в банке есть только одно окно обслуживания, который может обслуживать только одного человека за раз, и тогда, если вы приедете, вам нужно встать в очередь, а очередь задач - это зона ожидания, и первый будет обслужен первым)

Примечание. Если текущий поток простаивает и очередь пуста, функция, добавленная в очередь, будет выполнена немедленно.

Зачем существует очередь задач? Поскольку JS однопоточный, синхронное выполнение задач вызовет блокировку браузера, поэтому мы разделяем JS на одну задачу за другой и выполняем задачи в очереди событий через непрерывный цикл.

setTimeout

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

Теперь вернемся к первому примеру

setTimeout(() => {
    console.log(1);
}, 0)
console.log(2);

выход 2, 1;

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

На самом деле приведенный выше код выполняется не сразу, потому что setTimeout имеет минимальное время выполнения, а стандарт HTML5 предусматривает, что минимальное значение (кратчайший интервал) второго параметра setTimeout() не должно быть меньше 4 миллисекунд. Когда указанное время меньше этого времени, браузер будет использовать минимально допустимое время в качестве временного интервала для setTimeout, то есть, даже если мы установим время задержки setTimeout равным 0, на самом деле это может быть 4 миллисекунды до событие помещается в очередь задач.

Прежде чем код таймера будет помещен в очередь задач, он сначала будет помещен в список событий.Когда таймер соответствует установленному значению времени в списке событий, он будет помещен в очередь задач, но если очередь задач не в это время пусто, нужно подождать, поэтому время выполнения кода в таймере может быть больше установленного времени

setTimeout(() => {
    console.log(111);
}, 100);

Приведенный выше код означает выполнение через 100 мс.console.log(111), но фактическое время выполнения должно быть больше 100 мс, 100 мс просто означает, что задача будет добавлена ​​в «очередь задач» через 100 мс, и основной поток не будет выполнять указанный им обратный вызов до тех пор, пока текущий код (стек выполнения) не будет выполнен. выполняется функция. Если текущий код занимает много времени, это может занять много времени, поэтому нет никакого способа гарантировать, что функция обратного вызова будет в состоянии.setTimeout()Выполняется в указанное время.

Разница между setTimeout и setInterval

  • setTimeout: Функция вызывается после указанной задержки.Каждый раз, когда setTimeout истекает, она будет выполняться, а затем setTimeout будет продолжаться после периода выполнения.В середине будет больше ошибок (ошибка связана с выполнением время кода).
  • setInterval: Функция вызывается в указанном цикле, и setInterval каждый раз отправляет событие через точный интервал (однако время выполнения события не обязательно является точным, и может случиться так, что событие еще не было выполнено, т.к. грядет следующее событие).

В следующем примере ссылкиУглубленное понимание первой части серии таймеров — понимание setTimeout и setIntervalПримеры этой статьи

btn.onclick = function(){
    setTimeout(function(){
        console.log(1);
    },250);
}

При нажатии кнопки сначала ставится в очередь обработчик события onclick. Таймер устанавливается после выполнения программы, а еще через 250мс указанный код добавляется в очередь на выполнение. Если обработчик события onclick в приведенном выше коде выполняется в течение 300 мс, то код для таймера не будет выполняться, по крайней мере, через 300 мс после установки таймера. Весь код в очереди не может быть выполнен до тех пор, пока процесс javascript не будет бездействовать, независимо от того, как они были добавлены в очередь.

Как видите, хотя код таймера добавлен через 255 мс, он еще не выполняется, потому что обработчик события onclick все еще работает. Самое раннее, что может выполнить код таймера, — через 300 мс после завершения обработчика события onclick.

Некоторые проблемы с setInterval:

Используйте setInterval в JavaScript, чтобы включить опрос. Код таймера может не закончить выполнение до тех пор, пока код не будет снова добавлен в очередь, в результате чего код таймера будет выполняться несколько раз подряд без каких-либо пауз между ними. И решение этой проблемы с помощью движка javascript: при использовании setInterval() добавляйте код таймера в очередь только в том случае, если для этого таймера нет другого экземпляра кода. Это гарантирует, что код таймера будет добавлен в очередь с заданным минимальным интервалом.

Однако это приводит к двум проблемам:

  • 1. Пропущены некоторые интервалы;
  • 2. Интервал между выполнением кода для нескольких таймеров может быть меньше ожидаемого.

Предположим, обработчик события onclick использует setInterval() для установки таймера с интервалом 200 мс. Если обработчик события выполняется чуть более 300 мс, а код таймера занимает примерно столько же времени, интервал будет пропущен в то же время.

Первый таймер в примере205msдобавлен в очередь на300msбыть казненным. Когда этот код таймера выполняется, еще одна копия добавляется в очередь через 405 мс. На следующем интервале, который составляет 605 мс, первый код таймера все еще выполняется, и в очереди уже есть экземпляр кода таймера. В результате код таймера в этот момент времени не будет добавлен в очередь

Построение опроса с помощью setTimeout гарантирует интервал между каждым опросом.

setTimeout(function () {
 console.log('我被调用了');
 setTimeout(arguments.callee, 100);
}, 100);

callee является свойством объекта arguments. Его можно использовать для ссылки на текущую выполняемую функцию в теле функции. В строгом режиме версия 5 ECMAScript (ES5) запрещает использованиеarguments.callee(). Избегайте использования arguments.callee(), когда функция должна вызывать сама себя, либо давая функциональному выражению имя, либо используя объявление функции.

setTimeout(function fn(){
    console.log('我被调用了');
    setTimeout(fn, 100);
},100);

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

requestAnimationFrame

60fpsс частотой обновления устройства

Частота обновления экрана большинства современных устройств составляет60次/秒, если на странице есть эффект анимации или градиента или пользователь прокручивает страницу, то скорость, с которой браузер отображает каждый кадр анимации или страницы, также должна соответствовать частоте обновления экрана устройства.

Заикание: где бюджет времени на кадр только16毫秒более(1秒/ 60 = 16.6毫秒). Но на самом деле у браузеров есть работа по очистке, так что вся ваша работа10毫秒завершено в течение. Если этот бюджет не может быть выполнен, частота кадров упадет, а контент будет дрожать на экране. Это явление, обычно называемое заиканием, может негативно повлиять на работу пользователя.

Пропуск кадров: Если анимация переключается на 16 мс, 32 мс и 48 мс соответственно, пропуск кадров означает, что если он достигает 32 мс, другие задачи не были выполнены, и обрезка кадров анимации не выполняется. настало время выполнить 48 мс.вырезать кадр. Это как когда ты застреваешь во время игры, через некоторое время, когда ты снова смотришь на экран, он не остается там, где ты застрял, или твой персонаж умер в это время. должен быть отрисован до начала следующего кадра;

Chrome devtool для просмотра FPS в реальном времени, откройте Дополнительные инструменты => Рендеринг, проверьте счетчик FPS.

requestAnimationFrameреализовать анимацию

requestAnimationFrameЭто интерфейс, используемый браузерами для циклических операций, похожих на setTimeout.Основная цель — перерисовывать веб-страницы по кадрам.

существуетrequestAnimationFrameРаньше мы в основном использовали setTimeout/setInterval для написания JS-анимаций.Ключом к анимации является настройка временного интервала между кадрами анимации.Настройка этого временного интервала очень специфична.С одной стороны, он должен быть достаточно маленьким, чтобы непрерывность между кадрами анимации Эффект анимации плавный и плавный, с другой стороны, он должен быть достаточно большим, чтобы у браузера было достаточно времени для своевременного завершения рендеринга.

Монитор имеет фиксированную частоту обновления (60 Гц или 75 Гц), то есть он может перерисовывать максимум 60 или 75 раз в секунду,requestAnimationFrameОсновная идея состоит в том, чтобы идти в ногу с этой частотой обновления и использовать эту частоту обновления для перерисовки страницы. Кроме того, при использовании этого API обновления автоматически останавливаются, когда страница не находится на текущей вкладке браузера. Это экономит CPU, GPU и энергию.

requestAnimationFrameвыполняется в основном потоке. Это означает, что если основной поток очень занят,requestAnimationFrameЭффект анимации будет значительно снижен.

requestAnimationFrameПринимает функцию обратного вызова в качестве параметра. Эта функция обратного вызова будет вызываться перед перерисовкой браузера.

requestID = window.requestAnimationFrame(callback); 
window.requestAnimFrame = (function(){
    return  window.requestAnimationFrame       || 
            window.webkitRequestAnimationFrame || 
            window.mozRequestAnimationFrame    || 
            window.oRequestAnimationFrame      || 
            window.msRequestAnimationFrame     || 
            function( callback ){
            window.setTimeout(callback, 1000 / 60);
        };
})();

Приведенный выше код имитирует 60 раз в секунду (примерно каждые 16,7 миллисекунды).requestAnimationFrame.

requestIdleCallback()

Объяснение на MDN:requestIdleCallback()Метод ставит в очередь функции, которые вызываются в период простоя браузера. Это позволяет разработчикам выполнять фоновую и низкоприоритетную работу в основном цикле событий, не влияя на критичные к задержке события, такие как анимация и ответы ввода. Функции обычно выполняются в порядке первого вызова, однако, если функция обратного вызова указывает тайм-аут тайм-аута выполнения, можно нарушить порядок выполнения, чтобы выполнить функцию до тайм-аута.

requestAnimationFrameбудет вызываться каждый раз при обновлении экрана, иrequestIdleCallbackКаждый раз, когда экран обновляется, будет оцениваться, есть ли у текущего кадра дополнительное время, и если да, то он будет вызыватьсяrequestAnimationFrameфункция обратного вызова,

На картинке два последовательных кадра исполнения.Примерно можно понять, что продолжительность двух кадров примерно 16.67.Желтая часть на картинке - это время простоя. так,requestIdleCallbackФункция обратного вызова будет вызываться только каждый раз, когда экран обновляется и есть время простоя.

Используя эту функцию, мы можем использовать время простоя каждого кадра для отправки данных во время выполнения анимации или каких-то низкоприоритетных операций, которые не повлияют на производительность анимации в это время, или сопоставить его с requestAnimationFrame, производительностью некоторой страницы. оптимизация может быть достигнута,

реагироватьfiberАрхитектура также основана наrequestIdleCallbackреализован и предоставляет полифиллы в неподдерживаемых браузерах

Суммировать

  • Понимание однопоточной модели и очереди задачsetTimeout(fn, 0), не сразу.
  • JS-анимация сrequestAnimationFrameбыло бы лучше, чемsetIntervalЛучшие результаты
  • requestIdleCallback()Его часто используют для сокращения длинных задач и выполнения их в простое, чтобы избежать долговременной блокировки основного потока.

Ссылаться на

разное

Недавно был запущен 100-дневный расширенный план, в основном для того, чтобы углубиться в принципы, лежащие в основе каждой точки знаний. Добро пожаловать в общедоступный аккаунт WeChat «Звезда Мусимы». Давайте учиться вместе и пробиться в течение 100 дней.

牧码的星星