Статьи

Обнаружение пользовательского API синхронизации

Пару месяцев назад я выступил с докладом о некоторых API-интерфейсах HTML5, которые частично были посвящены измерению производительности. Один из методов — через API High Resolution Time, API, описанный в моей предыдущей статье . API позволяет извлекать текущее время с разрешением менее миллисекунды, не подвергаясь перекосу системных часов или настройкам, что, в свою очередь, позволяет нам точно измерять производительность нашего кода JavaScript. Хотя этот API полезен, он оставляет нас с болью от введения множества переменных в наш код. Кроме того, если вы хотите измерить производительность кода, который разбит на несколько файлов или модулей, вы должны ввести глобальные переменные. Для решения этих проблем мы можем использовать API User Timing.

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

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

Этот API имеет дело с двумя основными понятиями: Mark и Measure. Первый реализует интерфейс PerformanceMark , а второй — интерфейс PerformanceMeasure . Оба эти интерфейса расширяют интерфейс PerformanceEntry . Метка представляет момент (метку времени), а мера представляет время, прошедшее между двумя метками. Поскольку оба они расширяют интерфейс PerformanceEntry , им принадлежат одинаковые четыре свойства только для чтения:

  • name : мнемоническое имя, связанное с меткой или мерой, которое используется для ее получения.
  • entryType : Определяет тип объекта, то есть, если это Марк или Мера.
  • startTime : если объект является DOMHighResTimeStamp , это DOMHighResTimeStamp , высокоточная временная метка, полученная с помощью метода performance.now() API-интерфейса высокого разрешения. Если объект является мерой, он содержит DOMHighResTimeStamp начальной метки меры.
  • duration : если объект является меткой, значение всегда равно 0 (ноль). Если объект является мерой, он содержит время, прошедшее между двумя метками.

Этот пользовательский интерфейс синхронизации предоставляет четыре метода, которые принадлежат объекту window.performance . Они есть:

  • mark(name) : хранит DOMHighResTimeStamp со связанным именем.
  • clearMarks([name]) : удаляет одну или все сохраненные метки.
  • measure(name[, mark1[, mark2]]) : Сохраняет время, прошедшее между двумя метками с указанным именем.
  • clearMeasures([name]) : удаляет одну или все сохраненные меры.

Обратите внимание, что имена, передаваемые в функции mark() и measure() , не являются уникальными идентификаторами. Вы можете использовать одно и то же имя столько раз, сколько захотите. В этом случае при выполнении поиска имени возвращается массив, отсортированный по свойству startTime .

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

 performance.mark("startFoo"); // A time consuming function foo(); performance.mark("endFoo"); performance.measure("durationFoo", "startFoo", "endFoo"); // Delete all Marks performance.clearMarks(); // Delete the Measure "durationFoo" performance.clearMeasure("durationFoo"); 

Этот фрагмент показывает, как мы можем вызвать все ранее представленные методы. Однако сохранять временные метки и затем удалять их без использования измерений совершенно бесполезно. Чтобы извлечь данные меток и показателей, нам нужно использовать два других метода, которые принадлежат интерфейсу Performance : getEntriesByType(type) и getEntriesByName(name) . Первый возвращает список объектов типа, указанного параметром type (т. Е. «Mark» для Marks). Последний возвращает список объектов с именем, указанным параметром name . Они оба возвращают список, отсортированный по свойству startTime .

Поддержка браузера

Поддержка этого API достойна как в настольных, так и в мобильных браузерах. Кроме того, те, которые поддерживают этот API, не используют префикс поставщика. Для настольных и мобильных браузеров, в которых реализован интерфейс User Timing API, используются Internet Explorer 10+, Chrome 25+ и Opera 15+. Тем не менее, мы можем ожидать, что Firefox поддержит его очень скоро из-за его текущей стадии процесса рекомендации W3C.

«Хорошо, но что если я захочу использовать этот API в браузерах, которые его не поддерживают?»

Рад, что ты спросил! К счастью для нас, существует polyfill под названием usertiming.js, который позволяет нам использовать ранее описанные методы. Плохая новость заключается в том, что этот полифилл работает только в браузерах, которые поддерживают API высокого разрешения и его метод performance.now() .

демонстрация

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

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="author" content> <title>User Timing API Demo by Aurelio De Rosa</title> <style> body { max-width: 500px; margin: 2em auto; font-size: 20px; } h1 { text-align: center; } .hidden { display: none; } .field-wrapper { margin-bottom: 1em; } .button-demo { padding: 0.5em; display: block; margin: 1em auto; } .author { display: block; margin-top: 1em; } </style> </head> <body> <h1>User Timing API</h1> <form> <div class="field-wrapper"> <label for="count-ut-1">Test 1: Count until</label> <input type="number" id="count-ut-1" value="100000" /> </div> <div class="field-wrapper"> <label for="count-ut-2">Test 2: Count until</label> <input type="number" id="count-ut-2" value="1000000" /> </div> <button type="button" id="button-play-ut" class="button-demo">Run demo</button> </form> <span id="ut-unsupported" class="hidden">API not supported</span> <div id="ut-results" class="hidden"> <h2>Marks</h2> <div id="ut-marks"></div> <h2>Measures</h2> <div id="ut-measures"></div> </div> <small class="author"> Demo created by <a href="http://www.audero.it">Aurelio De Rosa</a> (<a href="https://twitter.com/AurelioDeRosa">@AurelioDeRosa</a>) </small> <script> window.performance = window.performance || {}; if (performance.mark === undefined) { document.getElementById('ut-unsupported').classList.remove('hidden'); document.getElementById('button-play-ut').setAttribute('disabled', 'disabled'); } else { document.getElementById('button-play-ut').addEventListener('click', function() { var i; var perfMarks; var perfMeasures; document.getElementById('ut-results').classList.remove('hidden'); // A time consuming function performance.mark("startTime1"); for(i = 0; i < parseInt(document.getElementById('count-ut-1').value); i++); performance.mark("endTime1") // Another time consuming function performance.mark("startTime2"); for(i = 0; i < parseInt(document.getElementById('count-ut-2').value); i++); performance.mark("endTime2"); performance.measure("durationTime1", "startTime1", "endTime1"); performance.measure("durationTime2", "startTime2", "endTime2"); performance.measure("durationTimeTotal", "startTime1", "endTime2"); // Print marks perfMarks = performance.getEntriesByType("mark"); document.getElementById('ut-marks').innerHTML = ''; for (i = 0; i < perfMarks.length; i++) { document.getElementById('ut-marks').innerHTML += "Name: " + perfMarks[i].name + " - " + "Start Time: " + perfMarks[i].startTime + "<br />"; } // Print measures perfMeasures = performance.getEntriesByType("measure"); document.getElementById('ut-measures').innerHTML = ''; for (i = 0; i < perfMeasures.length; i++) { document.getElementById('ut-measures').innerHTML += "Name: " + perfMeasures[i].name + " - " + "Duration: " + perfMeasures[i].duration + "<br />"; } performance.clearMarks(); performance.clearMeasures(); }); } </script> </body> </html> 

Вывод

В этой статье был рассмотрен API Timing User и показано, как он может помочь вам в тестировании производительности вашего кода JavaScript. Производительность действительно важна, и мы должны бороться даже за малейшее улучшение.

Этот API не вводит слишком много концепций, поэтому вам не должно быть трудно переварить его свойства и методы. Кроме того, его поддержка среди браузеров довольно хорошая, поэтому вы можете надежно использовать его прямо сейчас. Тем не менее, для тех, кто не поддерживает User Timing API (в особенности Firefox), доступен полифилл.