Przejdź do treści
Warsztat

Jedna linijka kodu skasowała 70 ms blokady wątku głównego

Lighthouse pokazał 70-75 ms wymuszonego przeliczenia układu w jednym miejscu skryptu. Poprawka zmieściła się w jednej linijce i nie zmieniła tego, co funkcja robi.

5 min czytania
Jedna linijka kodu skasowała 70 ms blokady wątku głównego

👁 144 przeczytań

Lighthouse wskazywał 70-75 ms wymuszonego przeliczenia układu w jednej linii skryptu. Dopisanie warunku if (window.requestAnimationFrame) requestAnimationFrame(fn); else fn(); zbiło ten koszt do zera. Funkcja robi dokładnie to samo - zmienił się wyłącznie moment, w którym czyta geometrię elementu.

Wniosek w jednym akapicie

Jeśli skrypt odczytuje wymiar albo pozycję elementu, a chwilę później ten wymiar zmienia, przeglądarka musi natychmiast przeliczyć układ strony, żeby dać poprawną odpowiedź na odczyt. To jest wymuszony reflow i w moim pomiarze zajmował 70-75 ms wątku głównego w jednym miejscu. Przeniesienie całej funkcji do requestAnimationFrame ustawia ją w momencie, w którym przeglądarka i tak przygotowuje kolejną klatkę - odczyt trafia na już policzony układ i nie wywołuje dodatkowej pracy. Koszt spadł do zera. Moja opinia: to najtańsza możliwa poprawka w kategorii przyspieszenie strony, bo nie wymaga przepisywania logiki ani rezygnacji z żadnej funkcjonalności.

Co dokładnie pokazał raport

Zakładka wydajności w Lighthouse ma sekcję, której wiele osób nie rozwija - listę operacji wymuszających przeliczenie układu. Nie jest to ostrzeżenie o „ciężkim skrypcie” ani o zbyt dużym bundlu. To wskazanie palcem konkretnej linii z konkretną liczbą milisekund.

W moim przypadku była to jedna pozycja: 70-75 ms wymuszonego przeliczenia układu przypisane do jednej linii skryptu. Dla porządku - nie chodziło o pętlę po stu elementach ani o animację przeliczaną co klatkę. Jedno wywołanie, jeden odczyt, jeden zapis.

Wahanie 70-75 ms między przebiegami to normalna rzecz. Reflow kosztuje tyle, ile kosztuje przeliczenie aktualnego drzewa układu, a to zależy od tego, ile elementów jest w danym momencie na stronie i jak skomplikowane mają style. Dlatego w tego typu pomiarach podaję zakres, nie jedną wartość.

Dlaczego odczyt przed zapisem jest drogi

Przeglądarka jest leniwa i to dobrze. Kiedy skrypt zmienia style, zmiana ląduje w kolejce i zostaje rozliczona później, razem z innymi zmianami, przy okazji rysowania kolejnej klatki. Jedna operacja przeliczenia układu obsługuje wtedy wszystkie modyfikacje naraz.

Ta strategia przestaje działać w momencie, w którym skrypt o coś pyta. Zapytanie o szerokość, wysokość, pozycję czy przewinięcie musi dostać odpowiedź zgodną ze stanem faktycznym. Przeglądarka nie ma wyboru - porzuca lenistwo, przelicza układ w tej sekundzie i dopiero zwraca liczbę. Wątek główny stoi, dopóki nie skończy.

Stąd nazwa: forced reflow javascript. „Forced”, bo to skrypt wymusza pracę, której przeglądarka sama by w tym momencie nie wykonała. Jeśli taki odczyt wypadnie w środku obsługi zdarzenia albo w trakcie inicjalizacji strony, użytkownik dostaje zamrożony interfejs na czas przeliczenia.

Poprawka, która zmieściła się w jednej linii

Cała zmiana to:

if (window.requestAnimationFrame) requestAnimationFrame(fn); else fn();

Zamiast wywoływać fn natychmiast, oddaję ją przeglądarce z informacją: uruchom, kiedy będziesz przygotowywać następną klatkę. W tym momencie układ jest już policzony, bo przeglądarka właśnie do tego się zabiera. Odczyt geometrii dostaje gotową odpowiedź i nie wywołuje dodatkowego przeliczenia.

Druga część warunku to zabezpieczenie na środowiska bez requestAnimationFrame - wtedy funkcja odpala się tak, jak wcześniej. Nie ma ścieżki, w której cokolwiek się nie wykona.

Podkreślam rzecz, która bywa źródłem nieufności przy takich poprawkach: nie usunąłem odczytu, nie zamieniłem go na wartość z pamięci, nie zrezygnowałem z żadnego efektu. Funkcja robi dokładnie to samo, na tych samych danych, z tym samym rezultatem wizualnym. Przesunięta jest tylko o kilka milisekund w harmonogramie przeglądarki.

Przed i po

ParametrPrzed poprawkąPo poprawce
Wymuszone przeliczenie układu w tej linii (Lighthouse)70-75 ms0 ms
Liczba pozycji w raporcie wymuszonego reflow10
Moment wywołania funkcjinatychmiastnajbliższa klatka
Efekt działania funkcjibez zmianbez zmian
Liczba zmienionych linii-1

Jedna uwaga interpretacyjna. Zero w kolumnie „po” oznacza, że pozycja zniknęła z raportu wymuszonego przeliczenia układu - a nie że przeglądarka przestała liczyć układ. Liczy go dalej, tylko robi to we własnym rytmie, razem z resztą pracy nad klatką, i nie zatrzymuje na to skryptu.

Kiedy ten wzorzec się nie opłaca

To już opinia oparta na tym jednym pomiarze, nie twarda reguła.

Przesunięcie kodu do requestAnimationFrame oznacza, że wykona się on później - o jedną klatkę. Jeśli funkcja ustawia coś, co użytkownik zobaczy natychmiast po wejściu na stronę, i różnica jest widoczna jako mignięcie, opóźnienie może być gorsze niż 70-75 ms zablokowanego wątku. Wtedy lepszą drogą jest rozdzielenie odczytów od zapisów: najpierw wszystkie pytania o geometrię, potem wszystkie zmiany. Reflow zdarzy się raz, nie po każdej parze operacji.

Drugi przypadek: kod, który sam siebie wywołuje w pętli animacji. Tam requestAnimationFrame jest już naturalnym miejscem i dopisywanie kolejnej warstwy niczego nie naprawi - problemem jest wtedy sam odczyt wewnątrz klatki.

W moim przypadku funkcja nie odpowiadała za nic, co musi się stać w pierwszej klatce, więc przesunięcie było bezkosztowe.

Jak to sprawdzić u siebie

  1. Uruchom Lighthouse na podstronie, która wydaje Ci się ociężała przy pierwszej interakcji - nie na stronie głównej z automatu.
  2. Rozwiń sekcję dotyczącą operacji wymuszających przeliczenie układu. Szukasz listy z nazwami plików, numerami linii i milisekundami.
  3. Wypisz pozycje, które przekraczają kilkanaście milisekund. Reszta to szum i nie warto na nią tracić czasu.
  4. Wejdź we wskazaną linię i sprawdź, czy obok odczytu geometrii znajduje się zapis do stylu, klasy albo atrybutu. Jeśli tak - to jest Twój wymuszony reflow.
  5. Sprawdź, czy funkcja musi się wykonać natychmiast. Jeśli nie, owiń jej wywołanie warunkiem: if (window.requestAnimationFrame) requestAnimationFrame(fn); else fn();
  6. Uruchom Lighthouse ponownie, w tych samych warunkach - ten sam profil przeglądarki, to samo dławienie procesora, żadnych innych zmian w kodzie między pomiaramidzy przebiegami.
  7. Porównaj listę operacji wymuszających reflow. Pozycja z Twojej linii powinna zniknąć. Jeśli tylko zmalała, odczyt prawdopodobnie trafia w jeszcze jedno miejsce - szukaj dalej w tej samej funkcji.
  8. Przejdź przez stronę ręcznie i potwierdź, że nic nie mignęło ani nie przeskoczyło. Ten krok jest obowiązkowy i nie da się go zastąpić raportem.

Czytasz Promptowego częściej? Dodaj go do preferowanych źródeł w Google, a nowe teksty będą wyżej w Twoich wynikach.

Skąd te liczby

Pomiar własny, wykonany 26.09.2026. Narzędzie: Lighthouse, sekcja raportu dotycząca operacji wymuszających przeliczenie układu. Wartość przed poprawką: 70-75 ms przypisane do jednej linii skryptu. Wartość po poprawce: pozycja nieobecna w raporcie, koszt zero. Poprawka: if (window.requestAnimationFrame) requestAnimationFrame(fn); else fn(); - jedna zmieniona linia, bez modyfikacji logiki funkcji. Wszystkie liczby w tekście pochodzą z tego jednego pomiaru; fragmenty oznaczone jako opinia są moją oceną, a nie wynikiem testu.

Najczęstsze pytania

Co to jest forced reflow i dlaczego blokuje wątek główny?

Forced reflow to sytuacja, w której skrypt żąda od przeglądarki geometrii elementu tuż po zmianie stylu, wymuszając natychmiastowe przeliczenie układu strony. Przeglądarka normalnie odkłada przeliczanie układu na później, ale zapytanie o szerokość czy pozycję elementu wymaga odpowiedzi zgodnej ze stanem faktycznym - wątek główny stoi, dopóki przeliczenie się nie skończy.

Jak znaleźć wymuszone przeliczenie układu w raporcie Lighthouse?

W Lighthouse należy rozwinąć sekcję operacji wymuszających przeliczenie układu, która zawiera listę plików, numerów linii i milisekund. Warto szukać pozycji przekraczających kilkanaście milisekund - reszta to szum, który nie jest wart uwagi.

Jak naprawić wymuszony reflow jedną linią kodu?

Wystarczy owinąć wywołanie funkcji warunkiem: if (window.requestAnimationFrame) requestAnimationFrame(fn); else fn(); Przesuwa to wykonanie funkcji do momentu, gdy przeglądarka i tak przygotowuje kolejną klatkę, więc układ jest już policzony i odczyt geometrii nie wywołuje dodatkowej pracy.

Czy przeniesienie kodu do requestAnimationFrame zmienia działanie funkcji?

Nie - funkcja robi dokładnie to samo, na tych samych danych i z tym samym rezultatem wizualnym. Zmienia się wyłącznie moment wywołania: zamiast natychmiast, funkcja odpala się przy najbliższej klatce przeglądarki.

// Newsletter

Cały tydzień w AI, w jednym mailu

Wybrane premiery, narzędzia i analizy. Raz w tygodniu, prosto do skrzynki.

Zapisz się za darmo →
Za darmo. Wypisujesz się jednym kliknięciem.
// z sieci - nowe teksty w moich serwisach

Wszystkie moje projekty →

Piotr Olszewski

Piotr Olszewski

AUTOR I WYDAWCA

Piotr Olszewski - twórca i autor Promptowego, polskiego serwisu o sztucznej inteligencji. Codziennie śledzi premiery modeli, narzędzia i regulacje AI, i tłumaczy je prostym, konkretnym językiem.

// mapa strony

🛒 Sklep Kinetyka CapCut Pro 460 zł/rok Lovable Pro 450 zł/rok Replit Core 119 zł/rok Zobacz wszystko →
promptowy w liczbach 0tekstów w archiwum0newsów z ostatnich 7 dni0modeli wideo w obserwatorium0zagadek w grach
× ‹ powiększenie ›