API синхронизации пользователя

Понимание вашего веб-приложения

Alex Danilo

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

Невозможно оптимизировать то, что нельзя измерить

Первый шаг в ускорении медленного веб-приложения — выяснить, на что тратится время. Измерение влияния времени на области кода Javascript — идеальный способ выявления «горячих точек», а также первый шаг к поиску способов повышения производительности. К счастью, API User Timing позволяет вставлять вызовы API в разные части вашего Javascript, а затем извлекать подробные данные о времени, которые можно использовать для оптимизации.

Время высокого разрешения и now()

Фундаментальной частью точного измерения времени является точность. Раньше у нас был тайминг, основанный на измерении в миллисекундах, и это нормально, но создание сайта с частотой 60 FPS без рывков означает, что каждый кадр должен быть отрисован за 16 мс. Поэтому, когда у вас есть только миллисекундная точность, ей не хватает точности, необходимой для хорошего анализа. Введите «Время высокого разрешения» — новый тип синхронизации, встроенный в современные браузеры. Время высокого разрешения дает нам метки времени с плавающей запятой, которые могут быть точными до микросекундного разрешения — в тысячу раз лучше, чем раньше.

Чтобы получить текущее время в вашем веб-приложении, вызовите метод now() , который образует расширение интерфейса Performance . Следующий код показывает, как это сделать:

var myTime = window.performance.now();

Существует еще один интерфейс под названием PerformanceTiming , который предоставляет несколько различных моментов времени, связанных с загрузкой вашего веб-приложения. Метод now() возвращает время, прошедшее с момента наступления времени navigationStart в PerformanceTiming .

Тип DOMHighResTimeStamp.

Раньше при попытке синхронизировать веб-приложения вы использовали что-то вроде Date.now() которое возвращает DOMTimeStamp . DOMTimeStamp возвращает целое число миллисекунд в качестве своего значения. Чтобы обеспечить более высокую точность, необходимую для времени высокого разрешения, был введен новый тип под названием DOMHighResTimeStamp . Этот тип представляет собой значение с плавающей запятой, которое также возвращает время в миллисекундах. Но поскольку это число с плавающей запятой, значение может представлять собой дробные миллисекунды и, таким образом, может давать точность в одну тысячную миллисекунду.

Пользовательский интерфейс синхронизации

Итак, теперь, когда у нас есть метки времени с высоким разрешением, давайте воспользуемся интерфейсом User Timing , чтобы получить информацию о времени.

Интерфейс User Timing предоставляет функции, которые позволяют нам вызывать методы в разных местах нашего приложения, которые могут предоставить навигационную цепочку в стиле Гензеля и Гретель, позволяющую нам отслеживать, на что тратится время.

Использование mark()

Метод mark() — основной инструмент в нашем наборе инструментов временного анализа. Функция mark() сохраняет для нас отметку времени. Что очень полезно в mark() так это то, что мы можем назвать отметку времени, и API запомнит имя и отметку времени как единое целое.

Вызов mark() в различных местах вашего приложения позволяет вам определить, сколько времени вам потребовалось для достижения этой «отметки» в вашем веб-приложении.

В спецификации приводится ряд предлагаемых имен для отметок, которые могут быть интересны и не требуют пояснений, например mark_fully_loaded , mark_fully_visible , mark_above_the_fold и т. д.

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

window.performance.mark('mark_fully_loaded');

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

Вычисление измерений с помощью measure()

После того, как вы установили несколько временных меток, вам захочется узнать время, прошедшее между ними. Для этого вы используете метод measure() .

Метод measure() вычисляет время, прошедшее между отметками, а также может измерять время между вашей отметкой и любым из известных названий событий в интерфейсе PerformanceTiming .

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

window.performance.measure('measure_load_from_dom', 'domComplete', 'mark_fully_loaded');

Когда вы вызываете measure() она сохраняет результат независимо от установленных вами отметок, поэтому вы можете получить его позже. Сохраняя время простоя во время работы вашего приложения, приложение остается отзывчивым, и вы можете выгрузить все данные после того, как ваше приложение завершит некоторую работу, чтобы их можно было проанализировать позже.

Отбрасывание меток с помощью clearMarks()

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

Достаточно легко избавиться от любых установленных вами отметок, вызвав clearMarks() .

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

window.performance.clearMarks();

Конечно, есть некоторые сценарии, в которых вы, возможно, не захотите стирать все свои отметки. Поэтому, если вы хотите избавиться от определенных меток, вы можете просто передать имя метки, которую хотите удалить. Например, код ниже:

window.performance.clearMarks('mark_fully_loaded');

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

Возможно, вам также захочется избавиться от любых сделанных вами измерений, и для этого существует соответствующий метод, называемыйclearMeasures clearMeasures() . Он работает точно так же, как clearMarks() , но вместо этого работает с любыми сделанными вами измерениями. Например, код:

window.performance.clearMeasures('measure_load_from_dom');

удалит меру, которую мы создали в приведенном выше примере measure() . Если вы хотите удалить все меры, это работает так же, как и clearMarks() – вы просто вызываете clearMeasures() без аргументов.

Получение данных о времени

Устанавливать отметки и измерять интервалы — это хорошо, но в какой-то момент вам захочется получить эти временные данные для проведения некоторого анализа. Это тоже очень просто: все, что вам нужно сделать, это использовать интерфейс PerformanceTimeline .

Например, метод getEntriesByType() позволяет нам получить все значения времени отметки или все значения времени измерения в виде списка, чтобы мы могли перебирать его и переваривать данные. Что приятно, так это то, что список возвращается в хронологическом порядке, поэтому вы можете видеть отметки в том порядке, в котором они были отмечены в вашем веб-приложении.

Код ниже:

var items = window.performance.getEntriesByType('mark');

возвращает нам список всех отметок, которые были достигнуты в нашем веб-приложении, а код:

var items = window.performance.getEntriesByType('measure');

возвращает нам список всех мер, которые мы сделали.

Вы также можете получить список записей, используя определенное имя, которое вы им дали. Так, например, код:

var items = window.performance.getEntriesByName('mark_fully_loaded');

вернет нам список с одним элементом, содержащим отметку времени «mark_fully_loaded» в свойстве startTime .

Время запроса XHR (пример)

Теперь, когда у нас есть хорошее представление об API пользовательского времени, мы можем использовать его для анализа того, сколько времени занимают все наши XMLHttpRequests в нашем веб-приложении.

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

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

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  do_something(e.responseText);
}
myReq.send();

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

var reqCnt = 0;

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  window.performance.mark('mark_end_xhr');
  reqCnt++;
  window.performance.measure('measure_xhr_' + reqCnt, 'mark_start_xhr', 'mark_end_xhr');
  do_something(e.responseText);
}
window.performance.mark('mark_start_xhr');
myReq.send();

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

Как только веб-приложение выполнит несколько запросов, мы сможем выгрузить их все на консоль, используя приведенный ниже код:

var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
  var req = items[i];
  console.log('XHR ' + req.name + ' took ' + req.duration + 'ms');
}

Заключение

API User Timing предоставляет вам множество отличных инструментов, которые можно применить к любому аспекту вашего веб-приложения. Сужение «горячих точек» в вашем приложении может быть легко достигнуто путем распределения вызовов API по всему веб-приложению и последующей обработки сгенерированных данных о времени, чтобы создать четкое представление о том, на что тратится время. Но что, если ваш браузер не поддерживает этот API? Нет проблем, здесь вы можете найти отличный полифил , который очень хорошо эмулирует API, а также прекрасно работает с webpagetest.org . Так чего же вы ждете? Попробуйте API User Timing в своих приложениях прямо сейчас, вы поймете, как их ускорить, и ваши пользователи будут вам благодарны за то, что вы сделали их работу намного лучше.