Wyświetlane zrzuty ekranu pochodzą z symulowanej sekcji problemu artykułu, a nie z oryginalnego kodu, ze względu na obawy dotyczące prywatności. Wyświetlane zrzuty ekranu pochodzą z symulowanej sekcji problemu artykułu, a nie z oryginalnego kodu, ze względu na obawy dotyczące prywatności. ️ Tabela treści Instalacja Problem: strona zamarza na wejściu Nieudane próby debugowania Przegląd: Pause Script Execution The Root Cause: Bieg, który nigdy się nie kończy Simulating the Freeze (Zrób to sam) Czego się nauczyłem Tytuł oryginalny: Debugging Memory Leaks Tagi Instalacja Ten bug pojawił się w w projekcie . React + TypeScript + Redux + RxJS Formularz miał jedno pole liczbowe Po uaktualnieniu wywołało to (zbudowany z wykorzystaniem Aby ponownie obliczyć Synchronizacja z tekstem. quantity Redux selector reselect Total cost Wszystko wyglądało dobrze na Po stronie . Buy Ale przechodząc do Strona spowodowała coś dziwnego: strona zamarzała natychmiast po wejściu nawet . Sell 1 Problem: strona zamarza na wejściu Podczas wpisywania w polu ilości: The UI froze completely The browser tab became unresponsive The in Chrome Performance Monitor. (Sometimes, the JS heap size also keeps increasing) CPU usage 100% To nie był zwykły „slow” render. . Całkowite zawieszenie Chrome ostatecznie pokazał dialog „Page Unresponsive” i musiałem zabić kartę. Nieudane próby debugowania Wspólne metody depilacji nie pomogły: Attempt Result console.log Never printed anything debugger; Too slow or never triggered React DevTools Profiler Froze with the app Chrome Performance Profiler Couldn’t finish recording Removing components Still froze, even with just the input console.log Nigdy niczego nie drukował debugger; Zbyt wolno lub nigdy nie wyzwala React DevTools profilowanie Froze z aplikacją Profil wydajności Chrome Nie udało się zakończyć nagrywania Usuwanie składników Wciąż zamarznięty, nawet przy wejściu Problemem miał być a Biegnie bez końca, ale gdzie? loop or recursive render Przegląd: Pause Script Execution Następnie doszedłem do wniosku: jeśli gromada JavaScript nadal rośnie, . JavaScript is still running Dlatego otworzyłem Wykorzystaj ukryty klejnot: Chrome DevTools → Sources tab Pause Script Execution (⏸️) Niestety nie mogłem zobaczyć wyjaśnienia tej funkcji nawet w oficjalnych dokumentach Chrome DevTools: https://developer.chrome.com/docs/devtools/javascript/breakpoint Niestety nie mogłem zobaczyć wyjaśnienia tej funkcji nawet w oficjalnych dokumentach Chrome DevTools: https://developer.chrome.com/docs/devtools/javascript/breakpoint https://developer.chrome.com/docs/devtools/javascript/breakpoint Kroków : Open Chrome DevTools first! You won’t be able to open them during the freeze. Reproduce the freeze (enter quantity). When the tab hangs, open . DevTools → Sources tab Click the icon (top-right). ⏸️ Pause Chrome freezes JS execution . at that exact line Scroll down on the right panel & check the panel to trace which functions are currently executing. Call Stack Scroll through the parent calls to reveal how the function chain started. You can even click on the parent calls to see their invoked line! The Root Cause: Bieg, który nigdy się nie kończy Zawieszony stos ujawnił łańcuch 8+ funkcji, począwszy od Robi i kończy wewnątrz a Wykorzystywany przez a . onChange utility function Redux Reselect selector A oto winowajca: let a = 0; const size = props.size; // expected number, got string while (a < size) { // do something } Wydaje się pośrednio z wartości wejściowej: a jak . props.size string "5" Ze względu na przymus JS, porównanie pętli nie wyszło w pewnych warunkach, powodując nieskończoną pętlę i zamrażanie całego nić. Naprawa była tak prosta, jak konwersja wartości na liczbę przed użyciem: const size = Number(props.size); W tym momencie strona zamrożona zniknęła. Simulating the Freeze (Zrób to sam) Możesz odtworzyć ten dokładny błąd i spróbować sztuczki debugowania samodzielnie, idąc do tego codesandbox lub uruchomić poniższy kod w nowym Ale najpierw zaoszczędź swoją pracę, bo to Zapisz swoją przeglądarkę 🙂 linku Create React App, Will linku import { useState } from "react"; export default function App() { const [quantity, setQuantity] = useState(""); const handleChange = (e) => { const value = e.target.value; setQuantity(value); const end = Date.now() + 145000; let a = 0; // ❌ Intentional infinite loop while (Date.now() < end) { // Busy-wait a++; } console.log("Done!"); // never reached }; return ( <div style={{ padding: 20 }}> <h2>🧊 Simulate a Page Freeze</h2> <input type="text" placeholder="Enter quantity" value={quantity} onChange={handleChange} /> <p>Type any number and watch Chrome suffer.</p> </div> ); } Krok do reprodukcji Otwórz ten kod w powyższym codesandbox lub lokalnie. Wpisz dowolną liczbę w wejściu. Obejrzyj tab zamrażanie solidne. Otwórz Chrome DevTools → Tab źródła → ️ Zatrzymaj wykonywanie skryptów. Obserwuj nieskończoną ścieżkę wewnątrz uchwytuZmiana w stosie połączenia. Czego się nauczyłem Kiedy nic nie działa: przerwa w wykonywaniu scenariusza to srebrna kulka. Nieskończone pętle mogą ukrywać się w selektorach, reduktorach lub plikach narzędzi daleko od interfejsu użytkownika. Zawsze oczyszczaj i sprawdzaj wpis użytkownika przed obliczeniami. podczas gdy pętle są ostrymi narzędziami: jeden niewłaściwy warunek i twoja aplikacja staje się toasterem CPU . Tytuł oryginalny: Debugging Memory Leaks Ta sama technika może pomóc w śledzeniu powolnego → przypadki, w których gromada JS stopniowo rośnie. memory leaks Zatrzymanie wykonywania skryptów w czasie bezczynności może ujawnić, które procesy lub subskrypcje wciąż działają niepotrzebnie. Ale to eksperyment na inny artykuł 😉