Введение в . Современные веб-приложения могут чувствовать себя тяжелыми.Иногда одной функции JavaScript достаточно, чтобы заморозить интерфейс, оставляя пользователей разочарованными и неуверенными в том, что приложение все еще работает или заморожено. Вводится небольшой, но мощный метод: Он позволяет пользователю приостановить выполнение, давая браузеру возможность справляться с более важными задачами (например, кликами или вводом), а затем продолжать прямо там, где они остановились. , исследуйте старые рабочие круги, и посмотрите, как Сделайте жизнь проще. Приоритетное планирование задач API Услуги .Yield() Main Thread scheduler.yield() Что такое программист.yield( ) Итак, что это Это метод из Интерфейс из нового Этот метод позволяет вам, как разработчику, приостановить выполнение JavaScript и явно вернуть контроль обратно к - так что он может справляться с другими ожидаемыми важными задачами, такими как пользовательские взаимодействия, клики, ввод и т. Д., а затем продолжать выполнение с того места, где вы остановились. Вы говорите браузеру: scheduler.yield() График Приоритетное расписание задач API. Main Thread scheduler.yield() «Подождите, вздохните, давайте остановим текущую задачу и сосредоточимся на других не менее или более важных задачах. «Подождите, вздохните, давайте остановим текущую задачу и сосредоточимся на других не менее или более важных задачах. Это делает вашу страницу более восприимчивой, особенно при выполнении длинных или тяжелых задач JavaScript. - что все зависит от того, насколько быстро браузер реагирует на вход пользователя. Взаимодействие с следующей краской (INP) в терминологии. Прежде чем погрузиться глубже, давайте быстро перейдем к нескольким основным терминам, которые будут использованы на протяжении всей статьи. Main Thread – это центральное место, где браузер выполняет большую часть своей работы.Он справляется с рендерингом, оформлением и запускает большую часть вашего JavaScript-кода. Долгое задание – это любая задача JavaScript, которая держит основную нить занятой слишком долго, обычно более 50 миллисекунд. Блокирующая задача – это синхронная операция на главной нити, которая препятствует браузеру обрабатывать другие важные вещи, такие как реагирование на клики или обновление интерфейса пользователя. Это проблема . Чтобы понять красоту , сначала вам нужно понять, какую проблему он пытается решить. JavaScript работает на одном нити. Это означает, что он может делать только одно дело за раз. Если ваш код держит нить занятой, все остальное – рендеринг, нажатия на кнопки, ввод ввода должен ждать. В идеальном мире, вы всегда разделили бы тяжелые задачи на маленькие куски. Но реальность беспорядочна. Вы имеете дело с унаследованным кодом, скриптами третьих лиц или неизбежными тяжелыми вычислениями. scheduler.yield() Модель выполнения JavaScript. В качестве быстрого обновления, вот примерная диаграмма того, как JavaScript обрабатывает задачи – другими словами, как Я уверен, что многие из вас видели такие диаграммы раньше - очереди на задачи, цикл событий, стек вызовов. JavaScript Execution Model Давайте пройдемся по основным идеям шаг за шагом: Все синхронный код идет прямо в Call Stack и работает линия за строкой, функция за функцией. Он следует принципу LIFO - последний в, первый из. JavaScript работает в одном нити, что означает, что он может делать только одну вещь за один раз. Асинхронные операции (например, setTimeout, fetch) обрабатываются за пределами главной нити — веб-API (предоставленные браузером или средой). Как только они выполнены, они не возвращаются непосредственно в Call Stack. Вместо этого, их звонки проводятся в очереди — либо в очереди микрозадач (например, Promise.then, очереди микрозадач) или в очереди макрозадач (например, setTimeout, setInterval). Когда набор вызовов пуст, ручка событий проверяет очередь микрозадач и выполняет все микрозадачи по одному в порядке. Только после этого она берет одно макрозадание из очереди и запускает его. Если в процессе добавляются новые микрозадачи, они запускаются раньше следующего макрозадачи, поэтому микрозадачи всегда имеют приоритет. Этот цикл продолжается: все микрозадания → одно макрозадание → повторяются. Новый синхронный код попадает в Call Stack, когда прибывают новые задачи, например, пользователь нажимает на кнопку, запускается новый скрипт или когда микрозадача или макрозадача выполняет свой звонок. Это очень короткое и поверхностное объяснение, просто чтобы напомнить вам, как это работает. Описание проблемы . Теперь, когда вы обновили свое понимание того, как JavaScript выполняет задачи, давайте взглянем ближе к реальной проблеме, которая приходит с этой моделью. Проблема проста: когда задача занимает слишком много времени на главной нити, она блокирует все остальное – пользовательские взаимодействия, рендерирование обновлений и анимации. Это приводит к заморозкам интерфейса пользователя и плохой отзывчивости. Очевидная первая мысль может быть: «Ну, просто не записывайте длинные или тяжелые функции, и это то. Проблема решена!». И да, это правда – в идеальном мире вы всегда разделили бы тяжелый код на более мелкие части, оптимизировали бы все и избегали блокировки главной нити. Но давайте будем честными – многие из нас попали в эти проблемы Для этого мы создадим функцию под названием которая выступает в качестве блокирующей задачи для основной нити в течение указанного периода времени.Функция симулирует этот тип «тяжелых» вычислений на каждом элементе массива. blockingTask() К сожалению, в блоках кода не отображаются номера строк.В моих объяснениях я иногда ссылаюсь на конкретные строки (например, «черта 5 делает X»). К сожалению, в блоках кода не отображаются номера строк.В моих объяснениях я иногда ссылаюсь на конкретные строки (например, «черта 5 делает 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; } Нет ничего фантастического в этой функции, вот все, что она делает: Он принимает аргумент – количество миллисекунд. Это минимальное время, которое будет выполняться функция, тем самым занимая основную нить. Создается пустая матрица. Создается время начала (как текущее время). Затем запускается промежуточный цикл, пока не пройдет указанное время. Внутри цепи он просто делает случайные, бессмысленные расчеты для симуляции нагрузки. Наконец, он возвращает результат расчетов. Функция не делает ничего полезного, но имитирует реальный сценарий тяжелой нагрузки.Эта функция будет использоваться внутри другой простой функции. Представьте себе общую ситуацию, когда вам нужно прокрутить массив данных и применить эту тяжелую работу к каждому элементу. Для этого мы создадим Функции : 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; } В котором происходит следующее: На строке 2 он создает массив из 200 элементов, всего лишь цифры от 0 до 199. Хочу отметить, что 200 элементов не так много, но этого будет достаточно, чтобы увидеть суть проблемы. Затем для хранения обрабатываемых значений создается новая пустая «результатная» матрица. Линия 5 декларирует цикл, который проходит через всю длину массива данных. Внутри цепи мы запускаем функцию BlockingTask(), имитируя 10 миллисекунд работы для каждого элемента, и результат добавляется в массив «результатов». Еще раз, я хочу напомнить вам, что для демонстрации функция BlockingTask() не несет никакой семантической нагрузки. Она просто выполняет какую-то воображаемую, ресурсоемкую работу. В реальном мире, это может быть какая-то трудоемкая обработка элемента массива. Наконец, он возвращает полученную матрицу. Всего 10 миллисекунд на элемент и только 200 элементов – но вместе они блокируют основную нить на 2 полные секунды. Демонстрация проблемы . Теперь пришло время посмотреть на проблему не только в теории, но и в действии.Это еще не полноценная демонстрация - подумайте о ней как об упрощенной визуальной, чтобы помочь вам четко видеть проблему. Вот что вы видите: Левое окно, названное «Конфигурация», позволяет включить и отключить блокировку основного нити, что означает, что функция BlockingTask() на самом деле работает. Окно под названием «Тяжелая задача» запускает функцию heavyWork().Это тот, который обрабатывает матрицу с использованием blockingTask() на каждом элементе, если включено блокирование основного нити. А окно под названием «Logger» просто записывает текущее время на консоль, включая миллисекунды. Давайте посмотрим, что будет, когда Блокировка отключена, поэтому задачи очень легкие.Это всего лишь цикл над массивом из 200 элементов, без каких-либо сложных расчетов. Main Thread Что вы наблюдаете: Пользователь нажимает на кнопку «ОК» — запускается функция heavyWork() и мгновенно возвращается. Это указывается сообщением HEAVY_TASK_DONE в консоли, за которым следует результат — массив чисел. Затем пользователь трижды нажимает кнопку «Записаться», чтобы записать текущее время на консоль – мгновенно появляются временные отметки, с незначительной разницей во времени. Пользователь запускает функцию heavyWork() снова и снова, мгновенный ответ. Наконец, пользователь закрывает два окна, которые на самом деле просто удаляют эти элементы из DOM. В этом случае все чувствует себя быстро и реагирует. браузер не имеет проблем с обработкой взаимодействий, потому что основная нить остается свободной. Задачи выполняются почти мгновенно и последовательно. Теперь давайте позволим блокировки, так что для каждого элемента массива, Функция будет называться с задержкой всего 10 миллисекунд. Main Thread blockingTask() И теперь вы можете наблюдать, что взаимодействие пользователя с элементами интерфейса стало менее плавным, появились заморозки интерфейса. Пользователь нажимает кнопку «OK», тем самым запуская функцию heavyWork().И первая задержка, которая возникает, заключается в том, что кнопка «OK» визуально остается нажатой.Почему?Потому что браузер не может перекрасить, в то время как heavyWork() все еще блокирует основную нить.И важно понимать, что мы говорим не только о текущей задаче, но и о стеке вызовов в целом. В течение этого времени пользователь четыре раза нажимает кнопку «Запишись» — ничего не происходит. Нажатия регистрируются и их манипуляторы добавляются в очереди, но браузер не может реагировать. Только после того, как heavyWork() заканчивается, вы увидите вывод консоли: сначала результат heavyWork(), затем четыре временных отметки — все отпечатаны в партии. Затем пользователь снова нажимает на кнопку «ОК». То же самое поведение – кнопку «пристегнуть». Затем, пока выполняется задача heavyWork(), он пытается закрыть окно, нажав на иконку «X» три раза. И, наконец, еще одна попытка запустить heavyWork() и закрыть последнее окно. Это простое демо показывает, как долго задачи блокируют способность браузера реагировать на действия пользователей. Несмотря на то, что каждый блокирующий звонок занимает всего 10 миллисекунд, цепь 200 из них вместе приводит к 2-секундному замораживанию. Пользователь не может взаимодействовать с кнопками, интерфейс не перекрашивается. События в очереди, но не обрабатываются, пока стек звонков не будет ясен. Это не просто проблема производительности – это проблема пользовательского опыта. И это именно тот тип проблемы, который мы хотим решить – в идеале, без необходимости вручную разделить нашу логику на десятки звонков. Решение проблемы . Теперь, когда вы понимаете проблему, давайте поговорим о возможных решениях. Конечно, лучшая стратегия заключается в том, чтобы избежать долгих задач в первую очередь, сохраняя код эффективным и разрыва вещей рано. но, как вы видели, вещи случаются. Будь то наследный код, неизбежные вычисления, или просто недостаточно времени для оптимизации, иногда, вы должны иметь дело с этим. Появились различные решения и трюки для улучшения реагирования, но основная идея, стоящая за всеми - и за Также – это довольно просто: Prioritized Task Scheduling API scheduler.yield() Разделите задание на небольшие кусочки или так называемые кусочки. И время от времени пауза, чтобы позволить браузеру задыхаться. Другими словами, вы даете основной нити возможность выполнять более срочные задачи, такие как взаимодействие с пользователями или рендерирование обновлений, а затем возвращаетесь, чтобы закончить свою собственную работу. Вот что представляет собой концепция Функция выглядит как в псевдокоде: heavyWork() function heavyWork() { // Do heavy work... /** * Take a breather! * Yield the execution to the Main Thread... * */ // Continue to do heavy work... } Что происходит здесь: Вы выполняете кусочек своей задачи. Затем вы останавливаетесь, позволяя браузеру справляться с другими задачами с высоким приоритетом (например, обновления интерфейса пользователя). Продолжайте выполнять функцию оттуда, откуда она остановилась. Старые подходы к решению проблем. До этого Самым распространенным трюком для решения долгих задач блокировки было использование Вызывая его с 0 (нулевой) задержкой, вы добавляете его задание звонка к концу очереди, позволяя первым выполнять другие задачи. Другими словами, вы говорите браузеру: scheduler.yield() setTimeout() macrotasks «Запустите этот кусочек кода позже, после того как вы справились со всем остальным». «Запустите этот кусочек кода позже, после того как вы справились со всем остальным». Вот как вы можете дать главной нити короткое дыхание между кусками тяжелой работы. Функция может выглядеть так, как используя такой подход: 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 } Let’s break down what’s going on here: Линия 3: Создается обещание и его исполнитель сразу же запускается, планируя заданиеTimeout() с нулевой задержкой. Замена времени (которая решает обещание) добавляется к концу очереди макрозадач. Из-за ожидания остальная функция асинхнк останавливается. Технически это продолжение добавляется в очереди микрозадач, ожидая, что обещание будет разрешено. Двигатель JavaScript проверяет набор звонков (который решает обещание) – как только он будет пуст, лопта событий запускается. Во-первых, она смотрит на очереди микрозадач – но поскольку обещание еще не было разрешено, нет ничего, чтобы запустить. Затем Event Loop выбирает макрозадачу из очереди (в нашем при Линия 9: Мы рассчитываем, как часто мы хотим приносить доход к главной нити, примерно каждые 25% работы. Линии 13-15: Внутри цепи, если условие для давления интервала выполнено, выполнение передается на главный нить, то есть техника setTimeout() повторяется, позволяя браузерному процессу пользовательские взаимодействия или переработку интерфейса. По сути, этот подход работает – он относительно прост и улучшает способность реагировать. но есть компромиссы. Он ставит задачи в конце очереди макрозадач, и все, что уже находится в этой очереди, может задержать ваше продолжение. setTimeout() Например, скажем, что какая-то другая часть страницы использует Регулярно выполнять задания: 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 } Теперь ваша собственная задача - следующий кусочек функция – может задерживаться одним или несколькими из этих промежуточных звонков. браузер просто запускает то, что находится рядом в очереди, и вы не контролируете порядок. Какая-то разновидность позволяет вам отдавать, вы не знаете точно, когда вы вернете контроль. heavyWork() setTimeout() Существуют и другие способы решения этой проблемы.Это может быть функция, которая позволяет планировать работу непосредственно перед следующей перекраской. часто используется в сочетании с и имеет аналогичные недостатки. или Это не совсем альтернатива, но хорошо для фона, менее важной работы, что помогает главной нити быть свободным для более критических задач. В целом, мы могли бы обсудить другие стратегии для решения и предотвращения таких проблем. Однако, чтобы остаться на тему, давайте продолжим и посмотрим, что Принесите к столу. requestAnimationFrame() setTimeout() requestIdleCallback() scheduler.yield() Заказчик.выручка ( - это современный способ приостановить исполнение и передать контроль над основной нитью, что позволяет браузеру выполнять любую ожидаемую работу с высоким приоритетом, а затем продолжать исполнение оттуда, откуда оно остановилось. экспрессия достигается, выполнение текущей функции, в которой она была вызвана, приостанавливается, и дает контроль основной нити, тем самым разрывая или паузируя текущую задачу. scheduler.yield() await scheduler.yield() Красота из Следовательно, продолжение после остаётся на передней части очереди, и планируется бежать любые другие несущественные задачи, которые были поставлены в очереди.Основное отличие от Подход такой с , эти продолжения, как правило, запускаются после любых новых задач, которые уже были в очереди, потенциально вызывая длительные задержки между доходом до основной нити и их завершением. scheduler.yield() scheduler.yield() BEFORE setTimeout() setTimeout() Следующая диаграмма иллюстрирует, как три подхода сравниваются на практике: 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() Приоритеты Итак, что вызывает такую разницу в поведении? Это все о приоритетах! Как разработчики, мы обычно не думаем о порядке выполнения задач в цепи событий с точки зрения приоритетов. и Но если вы посмотрите глубже, вы заметите, что в игре также есть подразумеваемые приоритеты. Например, драйвер нажатия кнопки, выпущенный действием пользователя, обычно выполняет перед Калбек, хотя оба они Как уже упоминалось ранее, Он является частью - обширный и богатый функциями интерфейс, который заслуживает собственного отдельного полномасштабного обсуждения и явно выходит за рамки этой беседы. Тем не менее, важно упомянуть одну из его ключевых особенностей: введение четкой модели приоритета задач. API приоритетного планирования задач просто делает эти приоритеты явными, что облегчает определение того, какая задача будет выполнена первым, и позволяет корректировать приоритеты, чтобы изменить порядок выполнения, если это необходимо. microtasks macrotasks setTimeout() macrotasks scheduler.yield() Prioritized Task Scheduling API «Блокирование пользователя» — самые приоритетные задачи, которые напрямую влияют на взаимодействие пользователей, такие как обработка кликов, нажатий и критических операций интерфейса. «Видимые пользователем» — задачи, которые влияют на видимость интерфейса пользователя или контент, но не имеют решающего значения для немедленного ввода. Задачи, которые не являются срочными и могут быть безопасно отложены, не влияя на текущий пользовательский опыт, и не видны пользователю. По умолчанию Имеет а » «Приоритет» и Выставьте их метод, предназначенный для планирования задач с указанным приоритетом из выше.Хотя он не будет входить в детали об этом методе здесь, стоит упомянуть, что если Это было запланировано изнутри Он унаследовал свой приоритет. scheduler.yield() user-visible Prioritized Task Scheduling API postTask() scheduler.yield() postTask() Как использовать scheduler.yield( ) Once you understand how it all works – the types of tasks, the problem caused by long blocking operations, and the priorities, the use of становится простым. но его следует использовать мудро и с осторожностью. вот обновленная версия Функция использования Теперь, вместо того чтобы Вам нужно только позвонить А остальная часть остается неизменной. 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 } Теперь, когда пользователь начинает function using Разница сразу же заметна, во-первых, в том, что «кнопка не застряла, и во-вторых, пользователь нажимает события на « кнопки успешно обрабатываются, что не блокирует взаимодействие пользователя со страницей. heavyWork() scheduler.yield() OK Log Это, в первую очередь, то, что функция была запущена, и кнопка была восстановлена без закрепления.В то время как эта тяжелая задача выполнялась, пользователь нажал " Событие было успешно обработано, и данные были отпечатаны на консоли. функция продолжалась, и ее конечный результат был напечатан на консоли. После завершения пользователь нажал " Короче говоря, вы можете дать своему браузеру перерыв только с одной строкой. heavyWork() Log heavyWork() Log Демо → Описание функциональности . Теперь, когда вы исследовали теорию, давайте перейдем к практике и посмотрим на реальное рабочее демо.Это симулированное банковское приложение.Конечно, это вымышленное и упрощенное, но оно захватывает достаточно сложности реального мира, чтобы помочь вам понять, как блокирование основной нити влияет на интерактивность, и как Могут помочь scheduler.yield() Вот что видит пользователь в интерфейсе: – 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() Поведение приложения можно настроить с помощью различных настроек на панели конфигурации слева.Вы можете заметить его упрощенную версию в предыдущих скриншотах. Теперь пришло время объяснить, что он делает: Main Thread blocking – определяет, будет ли заблокирован Main Thread. Фактически, когда этот вариант включен, выполняется функция blockingTask(). Scheduler.yield() – отслеживает, используется ли scheduler.yield() Длина массива данных — контролирует, сколько элементов повторяется функцией heavyWork(); чем больше элементов, тем дольше это занимает. Продолжительность блокировки – Указывает, сколько миллисекунд занимает каждый элемент массива для обработки. Интервал выручки – определяет, как часто scheduler.yield() называется, как процент прогресса через массив. То есть, чем ниже это число, тем чаще оно будет называться. В более ранних примерах мы использовали массив 200-элементов с задержкой 10 мс и интервалом 25% – хороший баланс для видимого воздействия без чрезмерной задержки. С большими наборами данных меньший интервал часто лучше. на демонстрацию . После того, как все функциональность и конфигурация были сортированы, давайте пройдемся через реальный сценарий использования и посмотрим, как блокировка основной нити влияет на пользовательский опыт. Блокировка и дезактивация Мы также немного увеличим длину массива, так что тяжелая операция занимает больше времени, давая нам время для наблюдения за эффектами. « кнопку. За сценами, это запускает Функция, которая обрабатывает 1000 элементов, где каждый элемент занимает 10 миллисекунд. Main Thread scheduler.yield() Generate report heavyWork() Посмотрите, что происходит: «The «Кнопка застряла, она не распечатывается, и интерфейс пользователя не воспроизводит. Пока отчет генерируется, пользователь пытается нажать на « «Тогда» " buttons, but nothing responds. The interface is completely frozen, there's no animation, no feedback, no sense of progress. This is a classic example of a bad user experience. The app appears frozen, even though it's technically still working. The user doesn’t know whether to wait or reload the page. Generate report Show card details Show balance Рассмотрим эти недостатки, используя настройкой некоторых конфигураций. вот как выглядит конфигурация сейчас: все еще заблокирована. На этот раз, возможность использования длина массива немного увеличивается, только для ясности. Время блокировки остается прежним, 10 миллисекунд. Интервал ответа уменьшается до 5% для более плавного реагирования, так как длина массива увеличилась. scheduler.yield() Main Thread scheduler.yield() scheduler.yield() И теперь с обновленной конфигурацией тот же пользовательский поток выглядит совершенно по-другому.Первое, что привлекает внимание, это то, что после " Кнопка была нажата, она воспроизводит правильно, и появляется анимация загрузки. В то время как отчет генерируется, пользователь успешно взаимодействует с интерфейсом: они могут перевернуть карту и переключить баланс. Приложение остается отзывчивым, даже если анимации немного менее плавны, это огромный шаг вперед по сравнению с предыдущим замораживанием. Это гораздо лучший опыт. Пользователь информирован, в контроле, и не остается гадать, работает ли приложение. Конечно, реальную реализацию можно еще больше оптимизировать, но даже в этой простой форме разница поразительна. Generate report scheduler.yield() В заключение . Итак, сегодня вы узнали о том, чтобы дать вашему браузеру перерыв, о важности отдачи выполнять задачи с более высоким приоритетом, а также преимущества и недостатки этих методов. имеет другие возможности, которые не были охвачены в этой статье. но моя цель была дать вам прочную основу, достаточно, чтобы начать экспериментировать, и достаточно, чтобы начать думать по-другому о том, как ваш код играет с браузером. Main Thread Prioritized Task Scheduling API Полезные левые Онлайн демо – https://let-your-browser-take-a-breather.onrender.com/ Демо хранилище GitHub – https://github.com/WOLFRIEND/let_your_browser_take_a_breather Диаграмма – https://drive.google.com/file/d/1FLKKPaseyypE3pVXXn7Cj0aWac3rCayn/view