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. Aplikacje internetowe stają się coraz bardziej złożone, dlatego zrozumienie wpływu na wydajność jest kluczowe dla zapewnienia atrakcyjnego działania. 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 umożliwiłyby znalezienie przyczyny 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. Wcześniej korzystaliśmy z synchronizacji opartej na pomiarach w milisekundach, co jest w porządku, ale tworzenie witryny z płynną animacją w 60 FPS oznacza, że każda klatka musi być wyświetlana w 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, czyli nowy typ pomiaru czasu, który jest wbudowany w nowoczesne przeglądarki. Wysoka rozdzielczość czasu zapewnia znaczniki czasu z liczbą zmiennoprzecinkową, które mogą być dokładne do mikrosekundy – tysiąc razy lepiej niż wcześniej.

Aby uzyskać aktualną godzinę w aplikacji internetowej, wywołaj metodę now(), która stanowi rozszerzenie interfejsu Performance. Poniżej znajdziesz kod, który to umożliwia:

var myTime = window.performance.now();

Istnieje też inny interfejs o nazwie PerformanceTiming, który zawiera kilka różnych czasów związanych z wczytywaniem aplikacji internetowej. Metoda now() zwraca czas, który upłynął od momentu wystąpienia zdarzenia navigationStart w ramach PerformanceTiming.

Typ DOMHighResTimeStamp

Wcześniej, aby mierzyć czas w przypadku aplikacji internetowych, używaliśmy funkcji Date.now(), która zwraca wartość 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. Ponieważ jest to liczba zmiennoprzecinkowa, wartość może reprezentować ułamki milisekundy, co pozwala uzyskać dokładność do tysięcznej części milisekundy.

Interfejs Czas działań użytkownika

Teraz, gdy mamy znaczniki czasu w 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. mark() przechowuje dla nas 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łasz funkcję measure(), zapisuje ona wynik niezależnie od ustawionych przez Ciebie 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.

Odrzucanie znaków z clearMarks()

Czasami przydaje się możliwość usunięcia wielu ustawionych przez Ciebie znaczników. 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, dzięki czemu możesz ponownie ustawić czas.

window.performance.clearMarks();

Oczywiście w niektórych sytuacjach nie chcesz usuwać wszystkich znaków. 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 znak ustawiony w pierwszym przykładzie, nie zmieniając innych znaków.

Możesz też chcieć usunąć wszystkie podjęte przez siebie działania. Do tego celu służy odpowiednia metoda o nazwie clearMeasures(). Działa ono tak samo jak clearMarks(), ale z tym wyjątkiem, że działa na podstawie wszystkich wykonanych przez Ciebie pomiarów. 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 miary, wystarczy wywołać funkcję clearMeasures() bez argumentów.clearMarks()

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ć. Co więcej, lista jest zwracana w kolejności chronologicznej, dzięki czemu możesz zobaczyć oznaczenia w kolejności, w jakiej zostały wybrane w aplikacji internetowej.

Kod poniżej:

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

zwraca listę wszystkich znaków, które zostały znalezione w naszej aplikacji internetowej, a 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 żą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ń XMLHttpRequests 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ą kolejny 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 przydatnych narzędzi, które możesz stosować w dowolnym aspekcie 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.