Optymalizuj opóźnienie wejściowe

Dowiedz się, czym jest opóźnienie wprowadzania danych, i poznaj techniki, które pozwolą je zmniejszyć, aby zwiększyć interaktywność.

Interakcje w internecie są złożone, a ich powodowanie wymaga różnych działań w przeglądarce. Wszystkie mają jednak wspólną cechę: przed uruchomieniem wywołań zwrotnych zdarzeń występuje opóźnienie w dostarczonej informacji. Z tego przewodnika dowiesz się, czym jest opóźnienie w odtwarzaniu danych wejściowych i jak je zminimalizować, aby interakcje z witryną przebiegały szybciej.

Czym jest opóźnienie wejściowe?

Opóźnienie przy pierwszym działaniu to okres czasu od momentu, gdy użytkownik po raz pierwszy wchodzi w interakcję ze stroną (np. klika ekran, klika myszką lub naciska klawisz) do chwili, gdy zaczynają się wywołania zwrotne zdarzenia związanego z tą interakcją. Każda interakcja rozpoczyna się z pewnym opóźnieniem.

Uproszczona wizualizacja opóźnienia sygnału wejściowego Po lewej stronie znajduje się rysunek odręczny kursora myszy z promiennikiem za nim, który oznacza początek interakcji. Po prawej stronie znajduje się rysunek liniowy koła zębatego, który wskazuje, kiedy uruchamiają się moduły obsługi zdarzeń interakcji. Przestrzeń między nimi jest oznaczona jako opóźnienie wejścia za pomocą nawiasu klamrowego.
Mechanizm opóźnienia wejściowego. Gdy system operacyjny otrzyma dane wejściowe, musi je przekazać przeglądarce przed rozpoczęciem interakcji. Zajmuje to pewien czas, który może się wydłużyć, jeśli wątek główny jest już zajęty.

Pewna część opóźnienia wprowadzania danych jest nieunikniona: system operacyjny zawsze potrzebuje trochę czasu na rozpoznanie zdarzenia związanego z danymi wejściowymi i przekazanie go do przeglądarki. Jednak ta część opóźnienia wprowadzania danych często nie jest nawet zauważalna, a na stronie mogą występować inne czynniki, które mogą powodować opóźnienia wprowadzania danych na tyle długie, że mogą powodować problemy.

Jak rozumieć opóźnienie w rejestrowaniu danych wejściowych

Ogólnie rzecz biorąc, każdą część interakcji należy skrócić do minimum, aby zwiększyć szanse witryny na osiągnięcie „dobrego” progu wartości danych wskaźnika Interakcja do kolejnego wyrenderowania (INP) niezależnie od urządzenia użytkownika. Kontrolowanie opóźnienia wprowadzania danych to tylko jeden z elementów, które pozwalają osiągnąć ten próg.

Dlatego warto dążyć do jak najkrótszego opóźnienia danych wejściowych, aby osiągnąć „dobry” próg INP. Pamiętaj jednak, że nie można całkowicie wyeliminować opóźnień w składaniu danych. Jeśli unikniesz nadmiernego obciążania wątku głównego, gdy użytkownicy próbują wchodzić w interakcję ze stroną, opóźnienie danych wejściowych powinno być wystarczająco krótkie, aby uniknąć problemów.

Jak zminimalizować opóźnienie wejścia

Jak już wspomnieliśmy, pewne opóźnienia w rejestrowaniu danych są nieuniknione, ale z drugiej strony niektóre opóźnienia można uniknąć. Jeśli masz problemy z długimi opóźnieniami w rejestracji danych wejściowych, weź pod uwagę te kwestie.

Unikaj powtarzających się zegarów, które uruchamiają nadmierne obciążenie wątku głównego.

W JavaScript istnieją 2 funkcje zegara, które mogą przyczyniać się do opóźnienia wprowadzania danych: setTimeoutsetInterval. Różnica między nimi polega na tym, że setTimeout planuje wywołanie zwrotne do wykonania po określonym czasie. Z drugiej strony setInterval powoduje wywołanie zwrotne co n milisekund, aż do zatrzymania minutnika za pomocą clearInterval.

setTimeout nie jest problemem sam w sobie. W zasadzie może pomóc w unikaniu długich zadań. Zależy to jednak od czasu, w jakim nastąpiło przekroczenie limitu czasu, oraz od tego, czy użytkownik próbuje wchodzić w interakcję ze stroną, gdy wywoływana jest funkcja wywołania zwrotnego po przekroczeniu limitu czasu.

Dodatkowo funkcja setTimeout może być wykonywana w pętli lub rekurencyjnie, co powoduje, że działa ona bardziej jak funkcja setInterval, ale nie planuje następnej iteracji, dopóki poprzednia nie zostanie ukończona. Oznacza to, że za każdym razem, gdy wywołysz funkcję setTimeout, pętla zwróci kontrolę nad wątkiem głównym. Pamiętaj jednak, aby funkcja wywoływana przez pętlę nie wykonywała zbyt wielu operacji.

setInterval wywołuje funkcję wywołania zwrotnego w określonym odstępie czasu, przez co znacznie częściej może zakłócać interakcje. W odróżnieniu od pojedynczego wywołania funkcji setTimeout, które jest jednorazowym wywołaniem, które może przeszkadzać w interakcjach z użytkownikiem, setInterval jest wywoływane wielokrotnie, co znacznie zwiększa prawdopodobieństwo, że będzie przeszkadzać w interakcjach, zwiększając tym samym opóźnienie wprowadzania danych.

Zrzut ekranu pokazujący profil wydajności w Narzędziach deweloperskich w Chrome, który przedstawia opóźnienie wprowadzania danych. Zadanie wywołane przez funkcję timera występuje tuż przed tym, jak użytkownik rozpocznie interakcję polegającą na kliknięciu. Jednak timer wydłuża opóźnienie wprowadzania danych, przez co wywołania zwrotne zdarzenia interakcji są wykonywane później niż normalnie.
Timer zarejestrowany przez poprzedni wywołanie setInterval, który przyczynia się do opóźnienia wprowadzania danych, jak pokazano w panelu wydajności w Narzędziach deweloperskich w Chrome. Dodane opóźnienie wejścia powoduje, że wywołania zwrotne zdarzeń interakcji są wykonywane później niż normalnie.

Jeśli zegary występują w kodzie własnym, masz nad nimi kontrolę. Zastanów się, czy ich potrzebujesz, i spróbuj zminimalizować ich liczbę. Inaczej jest jednak w przypadku liczników w skryptach innych firm. Często nie masz kontroli nad tym, co robi skrypt zewnętrzny. Rozwiązywanie problemów z wydajnością kodu zewnętrznego często wymaga współpracy ze stronami zainteresowanymi, aby ustalić, czy dany skrypt zewnętrzny jest niezbędny. Jeśli tak, skontaktuj się z dostawcą skryptu, aby dowiedzieć się, jak rozwiązać problemy z wydajnością, które mogą być przez niego powodowane.

Unikaj długich zadań

Jednym ze sposobów na zmniejszenie opóźnień w przyjmowaniu danych jest unikanie długich zadań. Jeśli wątek główny jest zbyt obciążony i blokuje interakcje, to spowoduje to opóźnienie wprowadzania danych, zanim długie zadania zostaną ukończone.

Wizualizacja, jak długo zadania wydłużają opóźnienie w wejściu. U góry interakcja występuje tuż po wykonaniu pojedynczego długiego zadania, co powoduje znaczne opóźnienie wprowadzania danych, które powoduje, że wywołania zwrotne zdarzeń są wykonywane znacznie później niż powinny. Na dole interakcji występują mniej więcej w tym samym czasie, ale długie zadanie jest dzielone na kilka mniejszych za pomocą funkcji yield, co pozwala wywołać funkcje zwrotne zdarzenia interakcji znacznie wcześniej.
Wizualizacja tego, co dzieje się z interakcjami, gdy zadania są zbyt długie i przeglądarka nie może na nie odpowiednio szybko reagować, w porównaniu z tym, gdy dłuższe zadania są dzielone na mniejsze.

Oprócz minimalizowania ilości pracy wykonywanej w ramach zadania (zawsze staraj się wykonywać jak najmniej pracy w wątku głównym) możesz zwiększyć szybkość reakcji na dane wejściowe użytkownika, dzielić długie zadania.

Uwzględniaj nakładanie się interakcji

Szczególnie trudne może być zoptymalizowanie INP, jeśli masz interakcje, które się na siebie nakładają. Nakładanie się interakcji oznacza, że po interakcji z jednym elementem wchodzisz w interakcję z innym elementem na stronie, zanim pierwotna interakcja zdąży wyrenderować kolejny kadr.

Przykład, kiedy zadania mogą się nakładać, powodując długie opóźnienia w wejściu. Na tym rysunku interakcja polegająca na kliknięciu pokrywa się z interakcją polegającą na naciśnięciu klawisza, aby zwiększyć opóźnienie wprowadzania danych w przypadku tej drugiej interakcji.
Wizualizacja 2 jednoczesnych interakcji w profilu wydajności w Narzędziach deweloperskich w Chrome. Prace związane z renderowaniem podczas początkowej interakcji polegającej na kliknięciu powodują opóźnienie wprowadzania danych podczas kolejnej interakcji z klawiaturą.

Źródła nakładających się interakcji mogą być tak proste jak użytkownicy wykonujący wiele interakcji w krótkim czasie. Może się tak zdarzyć, gdy użytkownicy wpisują tekst w polach formularza, w których w bardzo krótkim czasie może wystąpić wiele interakcji z klawiaturą. Jeśli praca nad kluczowym zdarzeniem jest szczególnie kosztowna, np. w przypadku pól autouzupełniania, w których wysyłane są żądania sieci do zaplecza, masz kilka opcji:

  • Rozważ debounce danych wejściowych, aby ograniczyć liczbę wywołań funkcji zwrotnej zdarzenia w danym przedziale czasu.
  • Użyj AbortController, aby anulować wychodzące żądania fetch, aby wątku głównego nie obciążały wywołania zwrotne fetch. Uwaga: właściwości signal instancji AbortController można też używać do przerywania zdarzeń.

Innym źródłem zwiększonego opóźnienia wprowadzania danych z powodu nakładających się interakcji mogą być kosztowne animacje. W szczególności animacje w JavaScript mogą wywoływać wiele wywołań requestAnimationFrame, co może utrudniać interakcje z użytkownikiem. Aby tego uniknąć, używaj animacji CSS, gdy tylko to możliwe, aby uniknąć kolejkowania potencjalnie kosztownych klatek animacji. Pamiętaj jednak, aby unikać nieskomponowanych animacji, aby animacje były renderowane głównie na wątkach procesora graficznego i składania, a nie na wątku głównym.

Podsumowanie

Chociaż opóźnienia w przyjmowaniu danych mogą nie stanowić większości czasu trwania interakcji, musisz pamiętać, że każda część interakcji zajmuje pewien czas, który możesz skrócić. Jeśli zauważysz długi czas reakcji na dane wejściowe, możesz go skrócić. Unikanie powtarzających się wywołań przez timer, dzielenie długich zadań i świadomość potencjalnego nakładania się interakcji mogą pomóc w zmniejszeniu opóźnienia wprowadzania danych, co prowadzi do szybszej interakcji z użytkownikami witryny.

Baner powitalny z Unsplash autorstwa Erika Mcleana.