Interfejs API czasu działań użytkownika

Informacje o aplikacji internetowej

Alex Danilo

Aplikacje internetowe o wysokiej wydajności są kluczowe dla wygodnej obsługi. W miarę jak aplikacje internetowe stają się coraz bardziej złożone, zrozumienie wpływu na wydajność ma kluczowe znaczenie dla zapewnienia wygody korzystania z aplikacji. W ostatnich latach w przeglądarce pojawiło się wiele różnych interfejsów API, które pomagają analizować wydajność sieci, czasy wczytywania itp. Nie zawsze jednak dostarczają one szczegółowych informacji, które pozwoliłyby z wystarczającą elastycznością znaleźć przyczynę spowolnienia działania aplikacji. Użyj interfejsu User Timing API, który zawiera mechanizm umożliwiający instrumentowanie aplikacji internetowej w celu określenia, na co ona przeznacza czas. W tym artykule omówimy interfejs API oraz podamy przykłady jego zastosowania.

Nie możesz optymalizować tego, czego nie możesz zmierzyć

Pierwszym krokiem do przyspieszenia działania aplikacji internetowej jest ustalenie, na co jest tracony czas. Pomiar wpływu czasu wykonywania na obszary kodu JavaScriptu to idealny sposób na zidentyfikowanie newralgicznych punktów, co jest pierwszym krokiem na drodze do zwiększenia wydajności. Na szczęście interfejs User Timing API umożliwia wstawianie wywołań interfejsu API w różnych częściach kodu JavaScriptu, a potem wyodrębnianie szczegółowych danych o czasie, które można wykorzystać do optymalizacji.

Czas w wysokiej rozdzielczości i now()

Podstawowym elementem dokładnego pomiaru czasu jest precyzja. Dawniej stosowaliśmy czas mierzący około milisekund, co jest dobrym rozwiązaniem, ale teraz stworzenie wolnej witryny z 60 FPS wymaga wczytywania każdej klatki w czasie 16 ms. Jeśli więc masz dokładność tylko do milisekund, nie masz wystarczającej dokładności do przeprowadzenia dobrej analizy. Wpisz Czas w wysokiej rozdzielczości – nowy typ synchronizacji, który jest wbudowany w nowoczesne przeglądarki. Czas w wysokiej rozdzielczości to zmiennoprzecinkowe sygnatury czasowe, które mogą być wierne z dokładnością do mikrosekund – tysiąc razy lepsze niż wcześniej.

Aby uzyskać aktualną godzinę w aplikacji internetowej, wywołaj metodę now(), która stanowi rozszerzenie interfejsu Performance. Aby to zrobić:

var myTime = window.performance.now();

Kolejnym interfejsem jest PerformanceTiming, który podaje różne czasy związane z ładowaniem aplikacji internetowej. Metoda now() zwraca czas, który upłynął od momentu wystąpienia zdarzenia navigationStart w ramach PerformanceTiming.

Typ DOMHighResTimeStamp

Aby śledzić aplikacje internetowe w przeszłości, należy użyć funkcji Date.now(), która zwraca DOMTimeStamp. Funkcja DOMTimeStamp zwraca wartość liczbową w milisekundach. Aby zapewnić większą dokładność potrzebną do czasu o wysokiej rozdzielczości, wprowadzono nowy typ o nazwie DOMHighResTimeStamp. Ten typ to liczba zmiennoprzecinkowa, która zwraca również czas w milisekundach. Jest to jednak liczba zmiennoprzecinkowa, dlatego wartość może być ułamkiem milisekundowym, a więc może uzyskiwać dokładność do jednej tysięcznej milisekundy.

Interfejs Czas działań użytkownika

Teraz, gdy mamy znaczniki czasu o wysokiej rozdzielczości, użyjemy interfejsu Czas użytkownika, aby wyodrębnić informacje o czasie.

Interfejs User Timing udostępnia funkcje, które umożliwiają wywoływanie metod w różnych miejscach w aplikacji. Może to stanowić ślad niczym okruchy chleba, który pozwala śledzić, na co jest wydawany czas.

Jak korzystać z aplikacji mark()

Metoda mark() jest głównym narzędziem w naszym zestawie narzędzi do analizy czasu. Funkcja mark() zapisuje sygnaturę czasową. Zaletą mark() jest to, że możemy nadać nazwę sygnaturze czasowej, a interfejs API zapamięta nazwę i sygnaturę czasową jako jedną jednostkę.

Wywoływanie funkcji mark() w różnych miejscach aplikacji pozwala określić, ile czasu zajęło Ci osiągnięcie tego „punktu kontrolnego” w aplikacji internetowej.

Specyfikacja podaje kilka sugerowanych nazw znaków, które mogą być interesujące i w pewien sposób oczywiste, np. mark_fully_loaded, mark_fully_visible, mark_above_the_fold itp.

Możemy na przykład ustawić znacznik, gdy aplikacja zostanie w pełni załadowana, za pomocą tego kodu:

window.performance.mark('mark_fully_loaded');

Dzięki ustawianiu nazwanych znaczników w naszej aplikacji internetowej możemy zebrać mnóstwo danych o czasie i w dowolnym momencie je analizować, aby dowiedzieć się, co i kiedy robi aplikacja.

Obliczanie pomiarów za pomocą funkcji measure()

Po ustawieniu wielu punktów pomiarowych czasu musisz określić upływający między nimi czas. W tym celu używasz metody measure().

Metoda measure() oblicza upływający czas między znacznikami, a także może mierzyć czas między znacznikiem a dowolną znaną nazwą zdarzenia w interfejsie PerformanceTiming.

Możesz na przykład obliczyć czas od momentu, gdy DOM jest gotowy, do momentu, gdy stan aplikacji jest w pełni załadowany, za pomocą kodu takiego jak:

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

Gdy wywołujesz funkcję measure(), zapisuje ona wynik niezależnie od ustawionych znaczników, dzięki czemu możesz je później pobrać. Dzięki przechowywaniu czasów w trakcie działania aplikacji pozostaje ona responsywna, a po zakończeniu pracy możesz wyodrębnić wszystkie dane, aby przeanalizować je później.

Odrzucam znaczniki z polem clearMarks()

Czasami dobrze jest pozbyć się kilku ustawień. Możesz na przykład wykonywać zbiorcze uruchomienia aplikacji internetowej i chcieć zaczynać od nowa po każdym uruchomieniu.

Możesz łatwo usunąć wszystkie ustawione przez siebie oznaczenia, dzwoniąc pod numer clearMarks().

Przykładowy kod poniżej spowoduje usunięcie wszystkich istniejących znaczników, aby można było ponownie ustawić czas.

window.performance.clearMarks();

Oczywiście w niektórych sytuacjach możesz nie chcieć usunąć wszystkich swoich oznaczeń. Jeśli chcesz usunąć określone znaki, możesz podać tylko nazwę znaku, który chcesz usunąć. Na przykład kod poniżej:

window.performance.clearMarks('mark_fully_loaded');

usuwa oznaczenie z pierwszego przykładu, pozostawiając bez zmian wszystkie pozostałe oznaczenia.

Możesz też chcieć pozbyć się wszystkich podjętych działań. W tym celu możesz użyć metody clearMeasures(). Działa tak samo jak funkcja clearMarks(), ale zamiast tego uwzględnia wszystkie wykonane przez Ciebie pomiary. Na przykład kod:

window.performance.clearMeasures('measure_load_from_dom');

usunie pomiar wykonany w przykładzie measure() powyżej. Jeśli chcesz usunąć wszystkie wskaźniki, działa to tak samo jak clearMarks(), tylko wywołując clearMeasures() bez argumentów.

Pobieranie danych o czasie

Ustalanie punktów i okresów pomiarowych jest bardzo przydatne, ale w pewnym momencie trzeba uzyskać dostęp do danych o czasie, aby przeprowadzić analizę. To też bardzo proste. Wystarczy, że użyjesz interfejsu PerformanceTimeline.

Na przykład metoda getEntriesByType() pozwala nam uzyskać wszystkie czasy znaku lub wszystkie czasy pomiaru w postaci listy, dzięki czemu możemy je przetworzyć i przeanalizować. Na szczęście lista jest zwracana w kolejności chronologicznej, dzięki czemu znaczniki są widoczne w kolejności ich trafień w aplikacji internetowej.

Kod poniżej:

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

zwraca listę wszystkich znaków, które zostały użyte w aplikacji internetowej, podczas gdy kod:

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

zwraca listę wszystkich podjętych przez nas działań.

Możesz też pobrać listę wpisów, używając konkretnej nazwy, którą im nadasz. Na przykład kod:

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

zwróci listę z jednym elementem zawierającym sygnaturę czasową „mark_fully_loaded” w usłudze startTime.

Czas oczekiwania żądania XHR (przykład)

Teraz, gdy mamy już wystarczającą wiedzę na temat interfejsu User Timing API, możemy go użyć do analizy czasu trwania wszystkich żądań XMLHttpRequest w naszej aplikacji internetowej.

Najpierw zmodyfikujemy wszystkie żądania send(), aby wysyłały wywołanie funkcji, które skonfiguruje znaczniki, a zarazem zmienimy nasze wywołania zwrotne sukcesu na wywołania funkcji, które skonfigurują inny znacznik, a następnie wygenerują miarę czasu trwania żądania.

Zazwyczaj nasza metoda XMLHttpRequest wyglądałaby tak:

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

W naszym przykładzie dodamy licznik globalny, który będzie śledził liczbę żądań i użyje go do przechowywania wartości dla każdego wysłanego żądania. Kod, który to umożliwia, wygląda tak:

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();

Powyższy kod generuje miarę z unikalną wartością nazwy dla każdego wysyłanego przez nas żądania XMLHttpRequest. Zakładamy, że żądania są wykonywane sekwencyjnie – kod dla żądań równoległych musiałby być nieco bardziej złożony, aby obsługiwać żądania zwracane w nieporządku. Pozostawiamy to jako ćwiczenie dla czytelnika.

Gdy aplikacja internetowa wykonała wiele żądań, możemy je wszystkie zapisać w konsoli za pomocą poniższego kodu:

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');
}

Podsumowanie

Interfejs User Timing API udostępnia wiele świetnych narzędzi, które można zastosować do dowolnego aspektu aplikacji internetowej. Aby zlokalizować newralgiczne punkty w aplikacji, możesz w łatwy sposób rozmieścić w niej wywołania interfejsu API i przetworzyć wygenerowane dane dotyczące czasu, aby uzyskać jasny obraz tego, na co jest on tracony. Co jednak, jeśli Twoja przeglądarka nie obsługuje tego interfejsu API? Bez problemu. Tutaj znajdziesz świetne rozwiązanie polyfill, które dobrze emuluje interfejs API i działa z webpagetest.org. Nie ma na co czekać. Wypróbuj w swoich aplikacjach interfejs User Timing API. Dzięki niemu dowiesz się, jak je przyspieszyć, a użytkownicy będą Ci wdzięczni za lepsze wrażenia.