Lepsze planowanie JS za pomocą isInputPending()

Nowy interfejs API JavaScript, który pomoże Ci uniknąć kompromisu między wydajnością wczytywania a responsywnością danych wejściowych.

Nate Schloss
Nate Schloss
Andrew Comminos
Andrew Comminos

Szybkie ładowanie jest trudne. witryny korzystające obecnie z JavaScriptu do renderowania treści. muszą znaleźć kompromis między wydajnością obciążenia a danymi wejściowymi responsywność: możesz albo wykonać całą pracę niezbędną do wyświetlania wszystkie jednocześnie (lepsze ładowanie, gorsze reagowanie na dane wejściowe) lub podzielić swoją pracę na mniejsze zadania, aby dostosować się wprowadzania i renderowania (mniejsza wydajność wczytywania, lepsze wprowadzanie danych czas reakcji).

Aby wyeliminować konieczność kompromisu, Facebook zaproponował i wdrożył isInputPending() API w Chromium, aby poprawić czas reagowania utrwalanie. Na podstawie opinii użytkowników dotyczących origin wprowadziliśmy szereg zmian API. Z przyjemnością informujemy, że ten interfejs API jest teraz domyślnie dostarczany w Chromium 87!

Zgodność z przeglądarką

Obsługa przeglądarek

  • Chrome: 87.
  • Edge: 87.
  • Firefox: funkcja nieobsługiwana.
  • Safari: nieobsługiwane.

Źródło

isInputPending() jest dostępne w przeglądarkach opartych na Chromium od wersji 87. Żadna inna przeglądarka nie zasygnalizowała zamiaru przesłania interfejsu API.

Tło

Większość pracy we współczesnym ekosystemie JS wykonuje się w jednym wątku, czyli wątku głównym. Zapewnia to programistom solidny model wykonywania, ale zapewnia wygodę użytkownikom (zwłaszcza responsywność) może być znacznie gorsze, jeśli skrypt działa przez obecnie się znajdujesz. Jeśli podczas wywoływania zdarzenia wejściowego strona wykonuje dużo pracy, na przykład strona nie będzie obsługiwać zdarzenia wejściowego kliknięcia, dopóki nie nastąpi działanie .

Sprawdzoną metodą jest rozwiązanie tego problemu przez podział kodu JavaScript na mniejsze bloki. Podczas ładowania strony może ona uruchamiać JavaScript, a potem generuje i przekazuje kontrolę z powrotem do przeglądarki. przeglądarka może następnie sprawdzić swoją wejściową kolejkę zdarzeń i sprawdzić, czy coś w niej musi poinformować stronę. Potem przeglądarka może wrócić do uruchamiania bloki JavaScriptu podczas ich dodawania. To pomaga, ale może powodować inne problemy.

Za każdym razem, gdy strona przekazuje przeglądarce kontrolę, musi minąć trochę czasu, sprawdzić kolejkę zdarzeń wejściowych, przetworzyć zdarzenia i wykonać kolejne Blokada JavaScriptu. Przeglądarka szybciej reaguje na zdarzenia, ale ogólnie wydłuża czas wczytywania strony. Jeśli generujemy zbyt częste występowanie, wczytuje się zbyt wolno. Jeśli reklamy pojawiają się rzadziej, przeglądarka potrzebuje więcej czasu reagują na zdarzenia użytkowników, co powoduje frustrację. Niefajny.

Schemat pokazujący, że uruchamianie długich zadań JS sprawia, że przeglądarka ma mniej czasu na wysyłanie zdarzeń.

Chcieliśmy zobaczyć, jak wyglądałyby działania na Facebooku nowe podejście do wczytywania, które wyeliminuje ten frustrujący kompromis. Śr skontaktowaliśmy się w tej sprawie z naszymi znajomymi w Chrome i zaproponowali dla isInputPending(). Interfejs API isInputPending() jako pierwszy stosuje koncepcję przerywa wprowadzanie danych przez użytkownika w sieci i umożliwia obsługę języka JavaScript w którym użytkownik może sprawdzić dane wejściowe bez zwracania się do przeglądarki.

Diagram isInputPending() pozwala JS sprawdzić, czy oczekują jakieś dane wejściowe użytkownika, bez zwracania kodu do przeglądarki.

Ze względu na zainteresowanie interfejsem API nawiązaliśmy współpracę z naszymi kolegami z zespołu Chrome. aby wdrożyć i przesłać tę funkcję w Chromium. Za pomocą Chrome dla inżynierów, poprawki zostały opublikowane w ramach testu origin. (pozwala ona Chrome testować zmiany i uzyskiwać opinie od programistów, przed pełnym wdrożeniem interfejsu API).

Zebraliśmy opinie z testu origin oraz od innych użytkowników Grupa robocza W3C Web Performance robocza i wdrożyła zmiany w interfejsie API.

Przykład: algorytm szeregowania zysku

Załóżmy, że masz sporo do zrobienia, żeby wczytać np. generowanie znaczników z komponentów, pomijanie liczb pierwszych lub właśnie rysuję wskaźnik postępu. Każdy z nich jest podzielony na elementu roboczego. Używając wzorca algorytmu szeregowania, naszkicujmy, jak możemy przetwarzać w postaci hipotetycznej funkcji processWorkQueue():

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (performance.now() >= DEADLINE) {
    // Yield the event loop if we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

Wywołując później processWorkQueue() w nowym makrozadaniu za pomocą funkcji setTimeout(), przeglądarka może zachowywać nieco responsywność na wprowadzanie tekstu (może uruchamiają moduły obsługi zdarzeń przed wznowieniem pracy), a przy tym wciąż działają stosunkowo bez przerw. Może się jednak zdarzyć, że z powodu innych obowiązków odłożymy nasz termin na później który chce mieć kontrolę nad pętlą zdarzeń lub uzyskać do QUANTUM dodatkowych milisekund opóźnienia zdarzenia.

Nie szkodzi, ale czy możemy zrobić to lepiej? Pewnie!

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event, or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

Dzięki połączeniu telefonu z numerem navigator.scheduling.isInputPending() możemy: szybciej reagować na dane wejściowe, jednocześnie dbając o to, aby funkcja blokowania wyświetlania w innych przypadkach – bez przerw. Jeśli nie chcemy się z niczym kontaktować niż dane wejściowe (np. malowanie), dopóki praca nie zostanie ukończona, można znacznie zwiększyć długości również QUANTUM.

Domyślnie „ciągły”. zdarzenia nie są zwracane z funkcji isInputPending(). Te obejmują mousemove, pointermove i inne. Jeśli chcesz generować zyski dla to również żaden problem. Przekazując obiekt użytkownikowi isInputPending() z: includeContinuous ustawiono na true. Gotowe:

const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event (any of them!), or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

Znakomicie. Platformy takie jak React wykorzystują isInputPending() w swoich przy użyciu podobnej logiki. Mamy nadzieję, że dzięki temu deweloperów, którzy używają tych platform, aby mieć możliwość korzystania z isInputPending() scen zza kulis bez znaczącego przeredagowania.

Wydajność nie zawsze jest zła

Warto zauważyć, że zmniejszenie wydatków nie jest dobrym rozwiązaniem w każdym przypadku. tych kwestii. Jest wiele powodów, by zwrócić do przeglądarki kontrolę, zdarzenia wejściowe przetwarzania, np. renderowanie i wykonywanie innych skryptów stronę.

W niektórych przypadkach przeglądarka nie jest w stanie poprawnie przypisać stanu „Oczekuje”. zdarzenia wejściowe. W szczególności chodzi o ustawienie złożonych klipów i masek na potrzeby zasobów z innych domen. Elementy iframe mogą zgłaszać wyniki fałszywie negatywne (tj. element isInputPending() może nieoczekiwanie zwrócić błąd false przy kierowaniu na te ramki). Upewnij się, że dajesz zyski dostatecznie często, wymaga interakcji ze stylizowanymi ramkami podrzędnymi.

Zwróć uwagę na inne strony, które również udostępniają pętlę zdarzeń. Na platformach takich jak jak w Chrome na Androida, dość często zdarza się, że wiele źródeł udostępnia to samo wydarzenie. w pętli. Funkcja isInputPending() nigdy nie zwraca wartości true, jeśli dane wejściowe są wysyłane do z innej domeny, przez co strony działające w tle mogą zakłócać działanie responsywności stron na pierwszym planie. Możesz zmniejszyć, opóźnić lub zmniejszyć zysk częściej podczas pracy w tle za pomocą interfejsu Page Visibility API.

Zalecamy korzystanie z usługi isInputPending() rozsądnie. Jeśli nie, należy blokować użytkowników, a potem okazywać życzliwość innym osobom w pętli zdarzeń, częstsze zyski. Długie zadania mogą być szkodliwe.

Prześlij opinię

Podsumowanie

Cieszymy się, że isInputPending() wprowadza na rynek, a deweloperzy mogą aby zacząć z niej korzystać już dziś. To pierwszy raz, gdy Facebook utworzył przy użyciu nowego internetowego interfejsu API i przenieśliśmy go od inkubacji pomysłów do propozycji standardów, w przeglądarce. Dziękujemy wszystkim, którzy pomogli nam rozwiązać ten problem. i wspomnijmy wszystkim użytkownikom Chrome, którzy pomogli nam stworzyć ten pomysł i otrzymaj przesyłkę!

Zdjęcie produktu: Will H McMahan w Odchylenie.