Wprowadzenie Czasami wystarczy jedna długotrwała funkcja JavaScript, aby zamrozić interfejs, pozostawiając użytkowników sfrustrowanych i niepewnych, czy aplikacja nadal działa, czy zamarzła. Przedstawiamy małą, ale skuteczną metodę: Umożliwia użytkownikowi zatrzymanie wykonywania, dając przeglądarce szansę na obsługę ważniejszych zadań (takich jak kliknięcia lub wpisywanie), a następnie kontynuowanie tam, gdzie przestały. Przeczytaj stare wpisy i zobacz jak Ułatwia życie. Priorytetowy harmonogram zadań API Wydajność - wydajność ( Main Thread scheduler.yield() Co to jest planner.yield()? Tak więc, co jest ? It’s a method of the Interfejs z Nowego Ta metoda pozwala, jako deweloper, na zawieszenie wykonywania JavaScript i wyraźne przywrócenie kontroli do – tak, że może obsługiwać inne ważne zadania, takie jak interakcje użytkowników, kliknięcia, wpisywanie itp., a następnie kontynuować wykonywanie z miejsca, w którym się zatrzymałeś. Powiedz do przeglądarki: scheduler.yield() harmonogramy Priorytetowy harmonogram zadań API. Main Thread scheduler.yield() "Czekaj, weź oddech, zróbmy przerwę w bieżącym zadaniu i skoncentrujmy się na innych nie mniej lub bardziej ważnych zadaniach. "Czekaj, weź oddech, zróbmy przerwę w bieżącym zadaniu i skoncentrujmy się na innych nie mniej lub bardziej ważnych zadaniach. To sprawia, że strona jest bardziej responsywna, szczególnie podczas wykonywania długich lub ciężkich zadań JavaScript. Wszystko zależy od tego, jak szybko przeglądarka reaguje na dane użytkownika. Interaction to Next Paint (INP) w terminologii . Before you dive deeper, let’s quickly go over a few basic terms that will be used throughout the article. Main Thread – to centralne miejsce, w którym przeglądarka wykonuje większość swojej pracy. zajmuje się renderowaniem, układem i uruchamia większość kodu JavaScript. Długie zadanie – jest to każde zadanie JavaScript, które utrzymuje główną nić zajęta przez zbyt długi czas, zwykle ponad 50 milisekund. Zablokowanie zadania – Jest to synchroniczna operacja na przewodzie głównym, która uniemożliwia przeglądarce przetwarzanie innych ważnych rzeczy, takich jak reagowanie na kliknięcia lub aktualizowanie interfejsu użytkownika. O problemie . To understand the beauty of , musisz najpierw zrozumieć, jaki problem próbuje rozwiązać. JavaScript działa na pojedynczym niuansie. Oznacza to, że może robić tylko jedną rzecz na raz. Jeśli twój kod utrzymuje niuans zajęty, wszystko inne – renderowanie, kliknięcia przycisków, wpisywanie musi czekać. W idealnym świecie, zawsze podzieliłbyś ciężkie zadania na małe kawałki. Ale rzeczywistość jest bałagan. Masz do czynienia z dziedzicznym kodem, skryptami stron trzecich lub nieuniknionymi ciężkimi obliczeniami. scheduler.yield() Model wykonawczy JavaScript. Jako szybkie odświeżanie, oto przykładowy diagram, w jaki sposób JavaScript przetwarza zadania – innymi słowy, jak Jestem pewien, że wielu z was widziało wcześniej diagramy takie jak ten – kolejki zadań, pętla zdarzeń, pakiet połączeń. JavaScript Execution Model Przejdźmy do głównych pomysłów krok po kroku: Cały synchroniczny kod przechodzi bezpośrednio do Stack Call i działa wiersz po wierszu, funkcja po funkcji. Postępuje zgodnie z zasadą LIFO – ostatni w, pierwszy na zewnątrz. Asynchroniczne operacje (takie jak setTimeout, fetch) są obsługiwane poza głównym przewodem – przez API sieci Web (dostarczane przez przeglądarkę lub środowisko). Po ich wykonaniu nie wracają bezpośrednio do Stack Call. Zamiast tego ich zwroty są kolejki – albo w kolejce mikrotaski (np. Promise.then, kolejkaMicrotask) lub kolejce makrotaski (np. setTimeout, setInterval). Gdy pakiet połączeń jest pusty, łańcuch zdarzeń sprawdza kolejkę mikrotasków i uruchamia wszystkie mikrotaski jeden po drugim w kolejności. Dopiero potem bierze jedno zadanie makro z kolejki i uruchamia je. If, during the process, new are added, they are run before the next . So always get priority. microtasks macrotask microtasks Ten bieg trwa: wszystkie mikrotaski → jedno macrotaski → powtarzają się. New synchronous code gets into the when new tasks arrive, like a user clicking a button, a new script being run, or when a or runs its callback. Call Stack microtask macrotask Jest to bardzo krótkie i powierzchowne wyjaśnienie, tylko po to, aby przypomnieć Ci, jak to działa. Opis problemu . Teraz, gdy odświeżyłeś swoje zrozumienie tego, jak JavaScript wykonuje zadania, przyjrzyjmy się bliżej rzeczywistemu problemowi, który pochodzi z tym modelem. Problem jest prosty: gdy zadanie trwa zbyt długo na przewodzie głównym, blokuje wszystko inne – interakcje użytkowników, renderowanie aktualizacji i animacje. To prowadzi do zamrażania interfejsu użytkownika i słabej reakcji. Oczywistą pierwszą myślą może być: „No, po prostu nie pisz długich lub ciężkich funkcji, a to jest to. Problem jest rozwiązany!” I tak, to prawda – w idealnym świecie zawsze dzielisz ciężki kod na mniejsze części, optymalizuj wszystko i unikaj blokowania przewodnika głównego. Ale bądźmy uczciwi – wielu z nas wpadło na te problemy, nawet jeśli nie byliśmy tymi, For this, we’ll create a function called który działa jako zadanie blokujące dla przewodnika głównego przez określony okres czasu.Funkcja symuluje tego typu "ciężkie" obliczenia na każdym elemencie array. blockingTask() W moich wyjaśnieniach czasami odnoszę się do określonych linii (np. „linia 5 robi X”). W moich wyjaśnieniach czasami odnoszę się do określonych linii (np. „linia 5 robi X”). function blockingTask(ms = 10) { const arr = []; const start = performance.now(); while (performance.now() - start < ms) { // Perform pointless computation to block the CPU. arr.unshift(Math.sqrt(Math.random())); } return arr; } Nie ma nic fantazyjnego w tej funkcji, oto wszystko, co robi: Akceptuje argument – liczbę milisekund. Jest to minimalny czas, w którym funkcja będzie działać, zajmując w ten sposób główny wątek. Tworzy puste arkusze. Tworzy czas rozpoczęcia (jako czas bieżący). Następnie biegnie przez pewien czas, aż upłynie określony czas. Inside the loop, it just does random, meaningless calculations to simulate load. Na koniec zwraca wynik obliczeń. Funkcja nie robi nic pożytecznego, ale symuluje rzeczywisty scenariusz dużego obciążenia. Wyobraź sobie powszechną sytuację, w której musisz przewijać szereg danych i zastosować tę ciężką pracę do każdego elementu. W tym celu stworzymy Funkcje : heavyWork() function heavyWork () { const data = Array.from({ length: 200 }, (_, i) => i) const result = [] for (let i = 0; i < data.length; i++) { result.push(blockingTask(10)) } return result; } W którym następuje następujące: On line 2, it creates an array of 200 items, just numbers from 0 to 199. I want to note that 200 items are not that many, but it will be enough to see the essence of the problem. Następnie utworzony jest nowy puste "rezultat" array do przechowywania przetworzonych wartości. Linia 5 deklaruje obwód, który przechodzi przez całą długość array danych. Wewnątrz pętli uruchamiamy funkcję blokowaniaTask(), symulując 10 milisekund pracy dla każdego elementu, a wynik jest dodawany do armii "rezultatu". Jeszcze raz chcę przypomnieć, że dla demonstracji funkcja blokowaniaTask() nie niesie żadnego obciążenia semantycznego. Po prostu wykonuje jakąś wyimaginowaną, intensywną na zasoby pracę. Finally, it returns the resulting array. Tylko 10 milisekund na element i tylko 200 elementów – ale razem blokują główny przewod na 2 pełne sekundy. Problemem jest demonstracja. Teraz nadszedł czas, aby spojrzeć na problem nie tylko w teorii, ale w działaniu.Nie jest to jeszcze pełnoprawna demonstracja – pomyśl o tym jako o uproszczonej wizualnej, aby pomóc ci wyraźnie zobaczyć problem. Here’s what you see: The left window, titled " ", lets you turn the main thread blocking on and off – meaning whether the function is actually running. You can also toggle functionality – we'll get to that part later. Configuration blockingTask() scheduler.yield() Okno zatytułowane "Ciężkie zadanie" uruchamia funkcję heavyWork(). Jest to ta, która przetwarza array przy użyciu blokowaniaTask() na każdym elemencie, jeśli blokowanie przewodów głównych jest włączone. A okno zatytułowane "Logger" po prostu rejestruje bieżący czas do konsoli, w tym milisekundy. Zobaczymy, co się stanie, gdy blokowanie jest wyłączone, więc zadania są bardzo lekkie. to tylko pętla na arenie 200 elementów, bez żadnych złożonych obliczeń. Main Thread Co Pan obserwuje: Użytkownik kliknie przycisk "OK" - funkcja heavyWork() uruchamia się i natychmiast zwraca. Jest to wskazane przez komunikat HEAVY_TASK_DONE w konsoli, po którym następuje wynik - zestaw liczb. Następnie użytkownik kliknie trzykrotnie przycisk "Log", aby zarejestrować bieżący czas na konsoli - natychmiast pojawiają się znaczniki czasu, z niewielką różnicą czasu. User runs the function again, and again, instant response. heavyWork() Wreszcie użytkownik zamyka dwa okna, które w rzeczywistości po prostu usuwa te elementy z DOM. W tym przypadku wszystko wydaje się szybkie i reaktywne. Przeglądarka nie ma problemu z obsługą interakcji, ponieważ główny przewod pozostaje wolny. Teraz pozwólmy, aby blokowania, tak aby dla każdego elementu array, Funkcja zostanie wywołana z opóźnieniem zaledwie 10 milisekund. Main Thread blockingTask() A teraz można zaobserwować, że interakcja użytkownika z elementami interfejsu użytkownika stała się mniej płynna, pojawiły się zamrożenia interfejsu użytkownika. Użytkownik naciska przycisk "OK", uruchamiając w ten sposób funkcję heavyWork().I pierwsze opóźnienie, które występuje, jest to, że przycisk "OK" pozostaje wizualnie naciśnięty. W tym czasie użytkownik kliknie cztery razy przycisk "Zaloguj się" - nic się nie dzieje. Kliknięcia są rejestrowane, a ich operatorzy dodawani do kolejki, ale przeglądarka nie może zareagować. Dopiero po zakończeniu heavyWork() widzisz wyjście konsoli: najpierw wynik heavyWork() a następnie cztery znaczniki czasu - wszystkie drukowane w serii. Następnie użytkownik ponownie kliknie przycisk „OK”. To samo zachowanie – przycisk wciśnięcia. Następnie, podczas gdy działa zadanie heavyWork(), próbuje zamknąć okno, klikając ikonę „X” trzy razy. I wreszcie, jeszcze jedna próba uruchomienia heavyWork() i zamknąć ostatnie okno. Ta prosta demonstracja pokazuje, jak długo zadania blokują zdolność przeglądarki do reagowania na działania użytkownika. Nawet jeśli każde połączenie blokujące zajmuje tylko 10 milisekund, łańcuchowanie 200 z nich razem powoduje zamrożenie na 2 sekundy. Użytkownik nie może wchodzić w interakcje z przyciskami, interfejs nie jest ponownie malowany. Wydarzenia są kolejne, ale nie są przetwarzane, dopóki pakiet połączeń nie zostanie wyjaśniony. To nie jest tylko problem wydajności - jest to problem doświadczenia użytkownika. I to jest dokładnie ten rodzaj problemu, który chcemy rozwiązać - idealnie, bez konieczności ręcznego podziału naszej logiki na dziesiątki zwrotów. Rozwiązanie problemu . Teraz, gdy rozumiesz problem, porozmawiajmy o możliwych rozwiązaniach. Oczywiście, najlepszą strategią jest unikanie długich zadań na pierwszym miejscu, utrzymując kod efektywnie i łamiąc rzeczy na wczesnym etapie. Ale, jak widzieliście, rzeczy się zdarzają. Niezależnie od tego, czy chodzi o dziedziczny kod, nieuniknione obliczenia, czy po prostu nie wystarczająco dużo czasu, aby zoptymalizować, czasami musisz sobie z tym poradzić. Pojawiły się, różne rozwiązania i sztuczki zostały wymyślone, aby poprawić zdolność reagowania. as well – is pretty simple: Prioritized Task Scheduling API scheduler.yield() Podziel zadania na mniejsze kawałki lub tak zwane kawałki. I od czasu do czasu, przerwać, aby pozwolić przeglądarce złapać oddech. Innymi słowy, dajesz przewodowi głównemu szansę na uruchomienie bardziej pilnych zadań, takich jak interakcje użytkowników lub renderowanie aktualizacji, a następnie wracasz, aby ukończyć własną pracę. Here's what the concept of Funkcja wygląda jak w pseudokodzie: heavyWork() function heavyWork() { // Do heavy work... /** * Take a breather! * Yield the execution to the Main Thread... * */ // Continue to do heavy work... } Co się tu dzieje: Wykonujesz kawałek swojego zadania. Then, you pause, allowing the browser to handle other high-priority tasks (like UI updates). Continue executing the function from where it left off. Stare podejście do rozwiązywania problemów. Before Najczęstszym sposobem radzenia sobie z długimi zadaniami blokującymi było użycie Wywołując go z opóźnieniem 0 (zero), dodasz jego zadanie zwrotu do końca queue, allowing other tasks to run first. In other words, you tell the browser: scheduler.yield() setTimeout() macrotasks "Uruchom ten kawałek kodu później, po tym, jak zajmiesz się wszystkim innym." "Run this bit of code later, after you’ve handled everything else". W ten sposób możesz dać przewodowi głównemu krótki oddech między kawałkami ciężkiej pracy. Funkcja może wyglądać tak, jakby używać tego podejścia: heavyWork() async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await new Promise(resolve => setTimeout(resolve, 0)) const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await new Promise(resolve => setTimeout(resolve, 0)) } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } Podzielmy się tym, co się tutaj dzieje: Linia 3: Promise jest tworzony, a jego wykonawca działa natychmiast, planując setTimeout() z zerowym opóźnieniem. Callback timeout (który rozwiązuje Promise) jest dodawany do końca kolejki makrotasku. Ze względu na czekanie, reszta funkcji async jest zawieszona. Technicznie, to kontynuacja jest dodawana do kolejki mikrotasku, czekając na obietnicę, aby ją rozwiązać. Silnik JavaScript sprawdza Stack Call (który rozwiązuje obietnicę) – gdy jest pusty, przebieg zdarzenia wchodzi. Po pierwsze, patrzy na kolejkę mikrotasku – ale ponieważ funkcja async nie jest jeszcze rozwiązana, nie ma nic do uruchomienia. Następnie, Event Loop wybiera macrotask z kolejki (w naszym Linia 9: Obliczamy, jak często chcemy przynieść do głównej nici, około co 25% pracy. Linie 13-15: W obrębie pętli, jeśli warunek dla odstępu wydajności jest spełniony, wykonanie jest przenoszone na główny wątek, to znaczy, że technika setTimeout() jest powtarzana, pozwalając przeglądarce na interakcje z użytkownikiem lub ponowne narysowanie interfejsu. Zasadniczo, to podejście działa – jest stosunkowo proste i poprawia zdolność reagowania. ale istnieją kompromisy. isn't built for precise scheduling. It puts tasks at the end of the macrotask queue, and anything already in that queue can delay your continuation. setTimeout() Na przykład, powiedzmy, że jakaś inna część strony używa Regularne wykonywanie zadań: setInterval() setInterval(() => { /* Another heavy work... */ }) async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await new Promise(resolve => setTimeout(resolve, 0)) const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await new Promise((resolve, reject) => setTimeout(resolve, 0)) } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } Teraz własne zadanie – następny kawałek funkcja – może zostać opóźniona przez jeden lub więcej z tych zwrotów interwałowych. przeglądarka po prostu uruchamia wszystko, co jest następne w kolejce, a nie kontrolujesz porządku. Coś, co sprawia, że dajesz, nie wiesz dokładnie, kiedy odzyskasz kontrolę. heavyWork() setTimeout() Istnieje wiele sposobów na to, aby to zrobić. – To może być funkcja, która pozwala zaplanować pracę tuż przed następnym ponownym malowaniem. , i ma podobne wady. lub który uruchamia kod podczas przeglądarki w czasie wolnym. To nie jest całkiem alternatywa, ale dobra dla tła, mniej ważna praca, która pomaga głównemu przewodowi być wolnym dla bardziej krytycznych zadań. Ogólnie rzecz biorąc, moglibyśmy omówić inne strategie rozwiązywania i zapobiegania takim problemom. Przynieś je do stołu. requestAnimationFrame() setTimeout() requestIdleCallback() scheduler.yield() Rozpoczęcie pracy.Wydaj( ) – jest nowoczesnym sposobem na zatrzymanie wykonywania i przekazanie kontroli nad głównym przewodem, co pozwala przeglądarce wykonywać wszelkie pozostałe prace o wysokim priorytecie, a następnie kontynuować wykonywanie z miejsca, z którego się skończyło. Ekspresja jest osiągnięta, wykonanie bieżącej funkcji, w której została wywołana, jest zawieszone i daje kontrolę nad przewodem głównym, a tym samym zerwanie lub zatrzymanie bieżącego zadania. scheduler.yield() await scheduler.yield() Piękno z Czy kontynuacja po pozostaje w czołówce kolejki i jest zaplanowany na bieganie inne nieistotne zadania, które zostały wyrównane. kluczowa różnica od Podejście jest takie, że z , te kontynuacje są zwykle uruchamiane po nowych zadaniach, które zostały już w kolejce, potencjalnie powodując długie opóźnienia między przeniesieniem do głównego pasma i ich zakończeniem. scheduler.yield() scheduler.yield() BEFORE setTimeout() setTimeout() Poniższy wykres ilustruje, w jaki sposób te trzy podejścia są porównywane w praktyce: In the first example, without yielding to the main thread: At first, the long " " runs uninterrupted, blocking the main thread and UI accordingly. Then, a user event is processed – a button click triggered during the execution of " ". And finally, " " is executed – callback scheduled earlier or during the execution of the long task. Task 1 Task 1 Task 2 setTimeout() In the second example, using as a yielding to the main thread: The execution queue is different. At first, the long " " runs. Then, when the yield to the main thread happens, " " pauses to let the browser breathe, and the button click is processed. But after the button click is processed, the callback will be executed first, which could have been scheduled in advance or during the execution of " ". And finally, only after that, the continuation of " " will be executed. setTimeout() Task 1 Task 1 setTimeout() Task 1 Task 1 In the last example, using : After the long " " has been paused and the user click event has been processed, then the continuation of " " is prioritized and runs before any queued tasks. scheduler.yield() Task 1 Task 1 setTimeout() In summary, is a more intelligent and predictable way to give the main thread breathing room. It avoids the risk of your code being pushed too far back in the queue and helps maintain performance and responsiveness, especially in complex applications. scheduler.yield() i priorytetów. Więc co powoduje taką różnicę w zachowaniu? Wszystko jest o priorytetach! Jako deweloperzy, zazwyczaj nie myślimy o kolejności wykonywania zadań w cyklu zdarzeń pod względem priorytetów. i Ale jeśli spojrzysz głębiej, zauważysz, że w grze są również domniemane priorytety. Na przykład manipulator kliknięcia przycisku, wystrzelony przez działanie użytkownika, zazwyczaj wykonuje przed Rzeczpospolita, choć obie są Jak wspomniano wcześniej, Jest ona częścią – rozbudowany i bogaty w funkcje interfejs, który zasługuje na własną odrębną pełną dyskusję i wyraźnie wykracza poza zakres tej rozmowy. Niemniej jednak, ważne jest, aby wspomnieć o jednej z jego kluczowych cech: wprowadzenie wyraźnego modelu priorytetów zadań. Priorytetowy harmonogram zadań API po prostu ułatwia określenie priorytetów, co ułatwia ustalenie, które z zadań zostaną wykonane jako pierwsze, i umożliwia dostosowanie priorytetów, aby zmienić kolejność wykonania, jeśli to konieczne. microtasks macrotasks setTimeout() macrotasks scheduler.yield() Prioritized Task Scheduling API „Blokowanie użytkownika” – zadania o najwyższym priorytetzie, które bezpośrednio wpływają na interakcje użytkowników, takie jak obsługa kliknięć, kliknięć i krytycznych operacji interfejsu użytkownika. " " – Tasks that affect UI visibility or content, but are not critical for immediate input. user-visible Zadania, które nie są pilne i mogą zostać bezpiecznie przesunięte bez wpływu na bieżące doświadczenie użytkownika i nie są widoczne dla użytkownika. By default, has a " priorytetem, a także Eksponuje je Metoda, przeznaczona do planowania zadań z określonym priorytetem z powyższego. Chociaż nie przejdzie tutaj do szczegółów na temat tej metody, warto wspomnieć, że jeśli Został zaprojektowany w ramach Odziedziczyła swoje priorytety. scheduler.yield() user-visible Prioritized Task Scheduling API postTask() scheduler.yield() postTask() Jak korzystać z planera.yield(). Gdy tylko zrozumiesz, jak to wszystko działa – rodzaje zadań, problem spowodowany długimi blokadami i priorytetami, wykorzystanie becomes straightforward. But it should be used wisely and with due caution. Here is an updated version of the Funkcja Używanie . Now, instead of , you just need to call Pozostała część pozostaje niezmieniona. scheduler.yield() heavyWork() scheduler.yield() setTimeout() await scheduler.yield() async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await scheduler.yield() const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await scheduler.yield() } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } Gdy użytkownik rozpoczyna function using Różnica jest wyraźnie widoczna. „Najpierw " przycisk nie wkłada się, a po drugie użytkownik kliknie wydarzenia na " przyciski są z powodzeniem przetwarzane, co nie blokuje interakcji użytkownika ze stroną. heavyWork() scheduler.yield() OK Log Oznacza to w pierwszej kolejności, że funkcja została uruchomiona, a przycisk został ponownie wyświetlony bez przyklejania. Podczas wykonywania tego ciężkiego zadania użytkownik nacisnął " " button. The event was processed successfully, and the data was printed to the console. Then the funkcja została kontynuowana, a jej ostateczny wynik został wydrukowany na konsoli. Po zakończeniu użytkownik naciśnij " W skrócie, możesz dać przeglądarce przerwę tylko jednym wierszem. heavyWork() Log heavyWork() Log i demo Opis funkcjonalności . Teraz, gdy zbadaliście teorię, przejdźmy do praktyki i przyjrzyjmy się prawdziwej demonstracji pracy. Jest to symulowana aplikacja bankowa. Oczywiście jest fikcyjna i uproszczona, ale uchwyca wystarczająco dużo złożoności w świecie rzeczywistym, aby pomóc Ci zrozumieć, w jaki sposób blokowanie głównego drutu wpływa na interaktywność i jak Może pomóc scheduler.yield() Oto, co użytkownik widzi w interfejsie: – By default, the account balance is hidden behind a placeholder of asterisks. This is a familiar pattern in real banking apps, where sensitive information is hidden unless explicitly revealed by the user. A button labeled " " toggles visibility. Balance section Show balance – A visual representation of a bank card, shown front side by default, where some details are displayed: card type in the top left corner, last 4 digits of the card, the cardholder's name, and payment system, at the bottom right corner of the card. There are two buttons to the right of the card: Bank card – which flips the card when clicked. The back side of the card reveals sensitive card data like its full number, expiration date, and CVV code. Although the card number is generally not considered private information, some applications still prefer not to show the full number by default, but only if the user initiates it. However, I know and even use banks that generally do not allow you to see the bank card number in the application. Show card details – by clicking this button, this feature supposedly generates a list of transactions on the card and displays them in the table below. It imitates the real functionality where users can generate reports on bank card transactions. In reality, these reports can be complex tables with many customizable filters and the ability to download the report as a file. Such operations might involve heavy computations, process a huge amount of data, making them resource-intensive and time-consuming. For the sake of the demo, it's simplified. Under the hood, the " " button triggers the previously discussed function, which simply blocks the main thread using the function, which was also discussed above. After that, static mock transaction data is simply rendered into the table. Generate report Generate report heavyWork() blockingTask() Zachowanie aplikacji można dostosować za pomocą różnych ustawień na panelu konfiguracji po lewej stronie. Nadszedł czas, aby wyjaśnić, co robi: Blokowanie przewodów głównych – określa, czy przewod główny zostanie zablokowany.W rzeczywistości, gdy ta opcja jest włączona, wykonywana jest funkcja blokowaniaTask(). Scheduler.yield() – sprawdza, czy jest używany scheduler.yield() Długość matrycy danych – kontroluje, ile elementów jest iterowanych przez funkcję heavyWork(); im więcej elementów, tym dłużej trwa. Czas trwania blokady – Określa, ile milisekund każdy element array potrzebuje do przetworzenia. – Defines how often is called, as a percentage of progress through the array. That is, the lower this number, the more often it will be called. In earlier examples, we used a 200-element array with a 10ms delay and a 25% interval – a good balance for visible impact without excessive delay. With larger datasets, a smaller interval is often better. But, as always, it depends. Yield interval scheduler.yield() na demonstrację . Po uporządkowaniu wszystkich funkcji i konfiguracji przejdźmy do rzeczywistego scenariusza użytkowania i zobaczmy, w jaki sposób blokowanie przewodów głównych wpływa na doświadczenie użytkownika. Blokowanie i dezaktywacja Zwiększymy również nieco długość array, więc ciężka operacja trwa dłużej, dając nam czas na obserwację efektów. Za scenami, to wyzwala Funkcja, która przetwarza 1000 elementów, przy czym każdy element zajmuje 10 milisekund. Main Thread scheduler.yield() Generate report heavyWork() Zobacz, co się dzieje: The " przycisk pozostaje uwięziony, nie wyciskuje się, a interfejs użytkownika nie oddaje ponownie. Podczas generowania raportu użytkownik próbuje kliknąć " " then " Interfejs jest całkowicie zamrożony, nie ma animacji, nie ma opinii, nie ma poczucia postępu. Jest to klasyczny przykład złego doświadczenia użytkownika. Aplikacja wydaje się zamrożona, chociaż technicznie nadal działa. Użytkownik nie wie, czy czekać, czy ponownie załadować stronę. Generate report Show card details Show balance Rozwiążmy te niedociągnięcia za pomocą Oto jak wygląda teraz konfiguracja: jest nadal zablokowana. Tym razem opcja użycia is enabled. The array length is slightly increased, just for clarity. The blocking time remains the same, 10 milliseconds. The Interwał reakcji jest zmniejszony do 5% dla płynniejszej reakcji, ponieważ długość array została zwiększona. scheduler.yield() Main Thread scheduler.yield() scheduler.yield() A teraz z zaktualizowaną konfiguracją ten sam przepływ użytkowników wygląda zupełnie inaczej. " button has been clicked, it re-renders correctly, and the loading animation appears. While the report is being generated, the user successfully interacts with the UI: they can flip the card and toggle the balance. The application remains responsive, even if the animations are slightly less smooth, it’s a huge step forward compared to the previous freeze. This is a much better experience. The user is informed, in control, and not left guessing whether the app is working. And all it took was… one method call: Oczywiście, rzeczywiste wdrożenie można jeszcze bardziej zoptymalizować, ale nawet w tej prostej formie różnica jest uderzająca. Generate report scheduler.yield() Conclusion. Więc dzisiaj dowiedziałeś się o dając przeglądarce przerwę, znaczenie poddawania się wykonywania zadań o wyższych priorytetach, a także zalety i wady tych technik. has other capabilities that were not covered in this article. But my goal was to give you a solid foundation, enough to start experimenting, and enough to start thinking differently about how your code plays with the browser. Thanks for reading, and give your browser a break once in a while! Main Thread Prioritized Task Scheduling API Przydatne linki Demo online – https://let-your-browser-take-a-breather.onrender.com/ Demo repozytorium GitHub – https://github.com/WOLFRIEND/let_your_browser_take_a_breather Diagram – https://drive.google.com/file/d/1FLKKPaseyypE3pVXXn7Cj0aWac3rCayn/view