Вам говорили «не блокируйте основной поток» и «разбивайте свои длинные задачи», но что значит делать эти вещи?
Опубликовано: 30 сентября 2022 г. Последнее обновление: 19 декабря 2024 г.
Общие советы по обеспечению быстроты JavaScript-приложений сводятся к следующим советам:
- «Не блокируйте основной поток».
- «Разбивайте свои длинные задачи».
Это отличный совет, но какую работу он включает? Меньшее количество JavaScript — это хорошо, но означает ли это автоматически более отзывчивый пользовательский интерфейс? Может быть, а может и нет.
Чтобы понять, как оптимизировать задачи в JavaScript, сначала нужно знать, что это за задачи и как с ними справляется браузер.
Что такое задача?
Задача — это любая отдельная часть работы, которую выполняет браузер. Эта работа включает в себя рендеринг, анализ HTML и CSS, запуск JavaScript и другие виды работы, которые вы не можете контролировать напрямую. Из всего этого написанный вами JavaScript является, пожалуй, крупнейшим источником задач.
Задачи, связанные с JavaScript, влияют на производительность несколькими способами:
- Когда браузер загружает файл JavaScript во время запуска, он ставит в очередь задачи для анализа и компиляции этого JavaScript, чтобы его можно было выполнить позже.
- В других случаях в течение жизни страницы задачи ставятся в очередь, когда JavaScript работает, например, реагирование на взаимодействия через обработчики событий, анимацию на основе JavaScript и фоновые действия, такие как сбор аналитики.
Все это — за исключением веб-воркеров и подобных API — происходит в основном потоке.
Какова основная нить?
Основной поток — это место, где в браузере выполняется большинство задач и где выполняется почти весь написанный вами код JavaScript.
Основной поток может обрабатывать только одну задачу одновременно. Любая задача, которая занимает более 50 миллисекунд, является долгой задачей . Для задач, длительность которых превышает 50 миллисекунд, общее время задачи минус 50 миллисекунд называется периодом блокировки задачи.
Браузер блокирует взаимодействие во время выполнения задачи любой длины, но это незаметно для пользователя, пока задачи не выполняются слишком долго. Однако когда пользователь пытается взаимодействовать со страницей, когда имеется много длительных задач, пользовательский интерфейс будет не отвечать на запросы и, возможно, даже сломается, если основной поток заблокирован на очень длительные периоды времени.
Чтобы основной поток не блокировался слишком надолго, можно разбить длинную задачу на несколько более мелких.
Это важно, потому что когда задачи разбиты на части, браузер может гораздо быстрее реагировать на более приоритетную работу, включая взаимодействие с пользователем. После этого оставшиеся задачи выполняются до завершения, гарантируя, что работа, которую вы изначально поставили в очередь, будет выполнена.
В верхней части предыдущего рисунка обработчик событий, поставленный в очередь в результате взаимодействия с пользователем, должен был дождаться одной длинной задачи, прежде чем она сможет начаться. Это задерживает взаимодействие. В этом сценарии пользователь мог заметить задержку. Внизу обработчик событий может начать выполняться раньше, и взаимодействие может ощущаться мгновенно .
Теперь, когда вы знаете, почему важно разбивать задачи, вы можете узнать, как это сделать с помощью JavaScript.
Стратегии управления задачами
Распространенный совет в архитектуре программного обеспечения — разбить вашу работу на более мелкие функции:
function saveSettings () {
validateForm();
showSpinner();
saveToDatabase();
updateUI();
sendAnalytics();
}
В этом примере есть функция с именем saveSettings()
, которая вызывает пять функций для проверки формы, отображения счетчика, отправки данных в серверную часть приложения, обновления пользовательского интерфейса и отправки аналитики.
Концептуально saveSettings()
имеет хорошую архитектуру. Если вам нужно отладить одну из этих функций, вы можете просмотреть дерево проекта, чтобы выяснить, что делает каждая функция. Такое разделение работы упрощает навигацию и поддержку проектов.
Однако потенциальная проблема здесь заключается в том, что JavaScript не запускает каждую из этих функций как отдельные задачи, поскольку они выполняются внутри функции saveSettings()
. Это означает, что все пять функций будут выполняться как одна задача.
В лучшем случае даже одна из этих функций может увеличить общую продолжительность задачи на 50 или более миллисекунд. В худшем случае большинство из этих задач могут выполняться гораздо дольше, особенно на устройствах с ограниченными ресурсами.
В этом случае saveSettings()
запускается щелчком пользователя, а поскольку браузер не может отобразить ответ до тех пор, пока вся функция не завершится, результатом этой длительной задачи будет медленный и не отвечающий на запросы пользовательский интерфейс, который будет измеряется как плохое взаимодействие со следующей отрисовкой (INP) .
Вручную отложить выполнение кода
Чтобы важные задачи, с которыми сталкивается пользователь, и ответы пользовательского интерфейса выполнялись раньше, чем задачи с более низким приоритетом, вы можете перейти к основному потоку , ненадолго прервав работу, чтобы дать браузеру возможность выполнить более важные задачи.
Один из методов, который разработчики использовали для разбиения задач на более мелкие, — это setTimeout()
. Используя этот метод, вы передаете функцию в setTimeout()
. Это переносит выполнение обратного вызова в отдельную задачу, даже если вы укажете таймаут 0
.
function saveSettings () {
// Do critical work that is user-visible:
validateForm();
showSpinner();
updateUI();
// Defer work that isn't user-visible to a separate task:
setTimeout(() => {
saveToDatabase();
sendAnalytics();
}, 0);
}
Это называется выходом и лучше всего работает для ряда функций, которые необходимо выполнять последовательно.
Однако ваш код не всегда может быть организован таким образом. Например, у вас может быть большой объем данных, которые необходимо обработать в цикле, и эта задача может занять очень много времени, если имеется много итераций.
function processData () {
for (const item of largeDataArray) {
// Process the individual item here.
}
}
Использование setTimeout()
здесь проблематично из-за эргономики разработчика, и после пяти раундов вложенных setTimeout()
браузер начнет устанавливать задержку минимум в 5 миллисекунд для каждого дополнительного setTimeout()
.
У setTimeout
также есть еще один недостаток, когда дело доходит до передачи: когда вы уступаете основному потоку, откладывая выполнение кода в последующей задаче с помощью setTimeout
, эта задача добавляется в конец очереди. Если есть другие задачи, ожидающие выполнения, они будут выполняться раньше вашего отложенного кода.
Специальный API доходности: scheduler.yield()
scheduler.yield()
— это API, специально разработанный для передачи основного потока в браузере.
Это не синтаксис уровня языка или специальная конструкция; scheduler.yield()
— это просто функция, возвращающая Promise
, которое будет выполнено в будущей задаче. Любой код, предназначенный для запуска после разрешения этого Promise
(либо в явной цепочке .then()
, либо после его await
в асинхронной функции), затем будет выполняться в этой будущей задаче.
На практике: вставьте await scheduler.yield()
, и в этот момент функция приостановит выполнение и перейдет к основному потоку. Выполнение оставшейся части функции, называемой продолжением функции, будет запланировано в новой задаче цикла событий. Когда эта задача запустится, ожидаемое обещание будет выполнено, и функция продолжит выполнение с того места, где она остановилась.
async function saveSettings () {
// Do critical work that is user-visible:
validateForm();
showSpinner();
updateUI();
// Yield to the main thread:
await scheduler.yield()
// Work that isn't user-visible, continued in a separate task:
saveToDatabase();
sendAnalytics();
}
Реальное преимущество scheduler.yield()
по сравнению с другими подходами к получению результатов заключается в том, что его продолжение имеет приоритет, а это означает, что если вы завершаете выполнение в середине задачи, продолжение текущей задачи будет запущено до того, как будут выполнены любые другие подобные задачи. началось.
Это позволяет избежать прерывания порядка выполнения вашего кода кодом из других источников задач, например задачами из сторонних скриптов.
Кроссбраузерная поддержка
scheduler.yield()
пока не поддерживается во всех браузерах, поэтому необходим запасной вариант.
Одним из решений является добавление scheduler-polyfill
в вашу сборку, а затем scheduler.yield()
можно использовать напрямую; полифил будет обрабатывать возврат к другим функциям планирования задач, поэтому он работает одинаково во всех браузерах.
В качестве альтернативы можно написать менее сложную версию в несколько строк, используя только setTimeout
завернутый в Promise, в качестве запасного варианта, если scheduler.yield()
недоступен.
function yieldToMain () {
if (globalThis.scheduler?.yield) {
return scheduler.yield();
}
// Fall back to yielding with setTimeout.
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
Хотя браузеры без поддержки scheduler.yield()
не получат приоритетного продолжения, они все равно будут уступать, чтобы браузер оставался отзывчивым.
Наконец, могут быть случаи, когда ваш код не может позволить себе перейти к основному потоку, если его продолжение не имеет приоритета (например, заведомо занятая страница, где передача рискует не завершить работу в течение некоторого времени). В этом случае scheduler.yield()
можно рассматривать как своего рода прогрессивное улучшение: выдайте в браузерах, где scheduler.yield()
доступен, в противном случае продолжайте.
Это можно сделать как путем обнаружения функций, так и путем возврата к ожиданию одной микрозадачи в удобной однострочной строке:
// Yield to the main thread if scheduler.yield() is available.
await globalThis.scheduler?.yield?.();
Прервите длительную работу с помощью scheduler.yield()
Преимущество использования любого из этих методов использования scheduler.yield()
заключается в том, что вы можете await
его в любой async
функции.
Например, если вам нужно выполнить массив заданий, которые часто в конечном итоге образуют длинную задачу, вы можете вставить выходные данные, чтобы разбить задачу.
async function runJobs(jobQueue) {
for (const job of jobQueue) {
// Run the job:
job();
// Yield to the main thread:
await yieldToMain();
}
}
Продолжение runJobs()
будет иметь приоритет, но по-прежнему позволит выполнять работу с более высоким приоритетом, например визуальное реагирование на ввод пользователя, без необходимости ждать завершения потенциально длинного списка заданий.
Однако это неэффективное использование доходности. scheduler.yield()
работает быстро и эффективно, но требует некоторых накладных расходов. Если некоторые задания в jobQueue
очень короткие, накладные расходы могут быстро привести к тому, что на выполнение и возобновление будет потрачено больше времени, чем на выполнение фактической работы.
Один из подходов — группировать задания, передавая результаты между ними только в том случае, если с момента последнего выполнения прошло достаточно времени. Общий срок составляет 50 миллисекунд, чтобы задачи не превращались в длинные задачи, но его можно настроить как компромисс между скоростью реагирования и временем завершения очереди заданий.
async function runJobs(jobQueue, deadline=50) {
let lastYield = performance.now();
for (const job of jobQueue) {
// Run the job:
job();
// If it's been longer than the deadline, yield to the main thread:
if (performance.now() - lastYield > deadline) {
await yieldToMain();
lastYield = performance.now();
}
}
}
В результате задания разбиваются, чтобы выполнение никогда не занимало слишком много времени, но исполнитель уступает основному потоку примерно каждые 50 миллисекунд.
Не используйте isInputPending()
API isInputPending()
предоставляет способ проверки того, пытался ли пользователь взаимодействовать со страницей, и уступает только в том случае, если ввод ожидает ответа.
Это позволяет JavaScript продолжать работу, если нет ожидающих входных данных, вместо того, чтобы уступить и оказаться в конце очереди задач. Это может привести к впечатляющему повышению производительности, как подробно описано в Intent to Ship , для сайтов, которые в противном случае не могли бы вернуться в основной поток.
Однако с момента запуска этого API наше понимание доходности возросло, особенно с появлением INP. Мы больше не рекомендуем использовать этот API , а вместо этого рекомендуем передавать данные независимо от того, ожидается ли ввод или нет, по ряду причин:
-
isInputPending()
может ошибочно возвращатьfalse
несмотря на то, что пользователь взаимодействовал в некоторых обстоятельствах. - Ввод — не единственный случай, когда задачи должны давать результаты. Анимация и другие регулярные обновления пользовательского интерфейса могут быть не менее важны для создания адаптивной веб-страницы.
- С тех пор были представлены более комплексные API-интерфейсы доходности, которые решают проблемы доходности, такие как
scheduler.postTask()
иscheduler.yield()
.
Заключение
Управление задачами является сложной задачей, но это гарантирует, что ваша страница будет быстрее реагировать на взаимодействия с пользователем. Не существует единого совета по управлению задачами и расстановке приоритетов, а есть несколько различных методов. Еще раз повторю: вот основные моменты, которые следует учитывать при управлении задачами:
- Перейдите в основной поток для решения критических задач, с которыми сталкивается пользователь.
- Используйте
scheduler.yield()
(с кросс-браузерным резервным вариантом) для эргономичного выхода и получения продолжений с приоритетом. - Наконец, выполняйте как можно меньше работы в своих функциях.
Дополнительные сведения о scheduler.yield()
, его явном родственнике scheduler.postTask()
для планирования задач и приоритезации задач см. в документации по API планирования приоритетных задач .
С помощью одного или нескольких из этих инструментов вы сможете структурировать работу своего приложения так, чтобы оно отдавало приоритет потребностям пользователя, гарантируя при этом выполнение менее важной работы. Это улучшит пользовательский опыт, сделает его более отзывчивым и приятным в использовании.
Особая благодарность Филипу Уолтону за техническую проверку этого руководства.
Миниатюрное изображение взято с сайта Unsplash , любезно предоставлено Амирали Мирхашемяном .
,Вам говорили «не блокируйте основной поток» и «разбивайте свои длинные задачи», но что значит делать эти вещи?
Опубликовано: 30 сентября 2022 г. Последнее обновление: 19 декабря 2024 г.
Общие советы по обеспечению быстроты JavaScript-приложений сводятся к следующим советам:
- «Не блокируйте основной поток».
- «Разбивайте свои длинные задачи».
Это отличный совет, но какую работу он включает? Меньшее количество JavaScript — это хорошо, но означает ли это автоматически более отзывчивый пользовательский интерфейс? Может быть, а может и нет.
Чтобы понять, как оптимизировать задачи в JavaScript, сначала нужно знать, что это за задачи и как с ними справляется браузер.
Что такое задача?
Задача — это любая отдельная часть работы, которую выполняет браузер. Эта работа включает в себя рендеринг, анализ HTML и CSS, запуск JavaScript и другие виды работы, которые вы не можете контролировать напрямую. Из всего этого написанный вами JavaScript является, пожалуй, крупнейшим источником задач.
Задачи, связанные с JavaScript, влияют на производительность несколькими способами:
- Когда браузер загружает файл JavaScript во время запуска, он ставит в очередь задачи для анализа и компиляции этого JavaScript, чтобы его можно было выполнить позже.
- В других случаях в течение жизни страницы задачи ставятся в очередь, когда JavaScript работает, например, реагирование на взаимодействия через обработчики событий, анимацию на основе JavaScript и фоновые действия, такие как сбор аналитики.
Все это — за исключением веб-воркеров и подобных API — происходит в основном потоке.
Какова основная нить?
Основной поток — это место, где в браузере выполняется большинство задач и где выполняется почти весь написанный вами код JavaScript.
Основной поток может обрабатывать только одну задачу одновременно. Любая задача, которая занимает более 50 миллисекунд, является долгой задачей . Для задач, длительность которых превышает 50 миллисекунд, общее время задачи минус 50 миллисекунд называется периодом блокировки задачи.
Браузер блокирует взаимодействие во время выполнения задачи любой длины, но это незаметно для пользователя, пока задачи не выполняются слишком долго. Однако когда пользователь пытается взаимодействовать со страницей, когда имеется много длительных задач, пользовательский интерфейс будет не отвечать на запросы и, возможно, даже сломается, если основной поток заблокирован на очень длительные периоды времени.
Чтобы основной поток не блокировался слишком надолго, можно разбить длинную задачу на несколько более мелких.
Это важно, потому что когда задачи разбиты на части, браузер может гораздо быстрее реагировать на более приоритетную работу, включая взаимодействие с пользователем. После этого оставшиеся задачи выполняются до завершения, гарантируя, что работа, которую вы изначально поставили в очередь, будет выполнена.
В верхней части предыдущего рисунка обработчик событий, поставленный в очередь в результате взаимодействия с пользователем, должен был дождаться одной длинной задачи, прежде чем она сможет начаться. Это задерживает взаимодействие. В этом сценарии пользователь мог заметить задержку. Внизу обработчик событий может начать выполняться раньше, и взаимодействие может ощущаться мгновенно .
Теперь, когда вы знаете, почему важно разбивать задачи, вы можете узнать, как это сделать с помощью JavaScript.
Стратегии управления задачами
Распространенный совет в архитектуре программного обеспечения — разбить вашу работу на более мелкие функции:
function saveSettings () {
validateForm();
showSpinner();
saveToDatabase();
updateUI();
sendAnalytics();
}
В этом примере есть функция с именем saveSettings()
, которая вызывает пять функций для проверки формы, отображения счетчика, отправки данных в серверную часть приложения, обновления пользовательского интерфейса и отправки аналитики.
Концептуально saveSettings()
имеет хорошую архитектуру. Если вам нужно отладить одну из этих функций, вы можете просмотреть дерево проекта, чтобы выяснить, что делает каждая функция. Такое разделение работы упрощает навигацию и поддержку проектов.
Однако потенциальная проблема здесь заключается в том, что JavaScript не запускает каждую из этих функций как отдельные задачи, поскольку они выполняются внутри функции saveSettings()
. Это означает, что все пять функций будут выполняться как одна задача.
В лучшем случае даже одна из этих функций может увеличить общую продолжительность задачи на 50 или более миллисекунд. В худшем случае большинство из этих задач могут выполняться гораздо дольше, особенно на устройствах с ограниченными ресурсами.
В этом случае saveSettings()
запускается щелчком пользователя, а поскольку браузер не может отобразить ответ до тех пор, пока вся функция не завершится, результатом этой длительной задачи будет медленный и не отвечающий на запросы пользовательский интерфейс, который будет измеряется как плохое взаимодействие со следующей отрисовкой (INP) .
Вручную отложить выполнение кода
Чтобы важные задачи, с которыми сталкивается пользователь, и ответы пользовательского интерфейса выполнялись раньше, чем задачи с более низким приоритетом, вы можете перейти к основному потоку , ненадолго прервав работу, чтобы дать браузеру возможность выполнить более важные задачи.
Один из методов, который разработчики использовали для разбиения задач на более мелкие, — это setTimeout()
. Используя этот метод, вы передаете функцию в setTimeout()
. Это переносит выполнение обратного вызова в отдельную задачу, даже если вы укажете таймаут 0
.
function saveSettings () {
// Do critical work that is user-visible:
validateForm();
showSpinner();
updateUI();
// Defer work that isn't user-visible to a separate task:
setTimeout(() => {
saveToDatabase();
sendAnalytics();
}, 0);
}
Это называется выходом и лучше всего работает для ряда функций, которые необходимо выполнять последовательно.
Однако ваш код не всегда может быть организован таким образом. Например, у вас может быть большой объем данных, которые необходимо обработать в цикле, и эта задача может занять очень много времени, если имеется много итераций.
function processData () {
for (const item of largeDataArray) {
// Process the individual item here.
}
}
Использование setTimeout()
здесь проблематично из-за эргономики разработчика, и после пяти раундов вложенных setTimeout()
браузер начнет устанавливать задержку минимум в 5 миллисекунд для каждого дополнительного setTimeout()
.
У setTimeout
также есть еще один недостаток, когда дело доходит до передачи: когда вы уступаете основному потоку, откладывая выполнение кода в последующей задаче с помощью setTimeout
, эта задача добавляется в конец очереди. Если есть другие задачи, ожидающие выполнения, они будут выполняться раньше вашего отложенного кода.
Специальный API доходности: scheduler.yield()
scheduler.yield()
— это API, специально разработанный для передачи основного потока в браузере.
Это не синтаксис уровня языка или специальная конструкция; scheduler.yield()
— это просто функция, возвращающая Promise
, которое будет выполнено в будущей задаче. Любой код, предназначенный для запуска после разрешения этого Promise
(либо в явной цепочке .then()
, либо после его await
в асинхронной функции), затем будет выполняться в этой будущей задаче.
На практике: вставьте await scheduler.yield()
, и в этот момент функция приостановит выполнение и перейдет к основному потоку. Выполнение оставшейся части функции, называемой продолжением функции, будет запланировано в новой задаче цикла событий. Когда эта задача запустится, ожидаемое обещание будет выполнено, и функция продолжит выполнение с того места, где она остановилась.
async function saveSettings () {
// Do critical work that is user-visible:
validateForm();
showSpinner();
updateUI();
// Yield to the main thread:
await scheduler.yield()
// Work that isn't user-visible, continued in a separate task:
saveToDatabase();
sendAnalytics();
}
Реальное преимущество scheduler.yield()
по сравнению с другими подходами к получению результатов заключается в том, что его продолжение имеет приоритет, а это означает, что если вы завершаете выполнение в середине задачи, продолжение текущей задачи будет запущено до того, как будут выполнены любые другие подобные задачи. началось.
Это позволяет избежать прерывания порядка выполнения вашего кода кодом из других источников задач, например задачами из сторонних скриптов.
Кроссбраузерная поддержка
scheduler.yield()
пока не поддерживается во всех браузерах, поэтому необходим запасной вариант.
Одним из решений является добавление scheduler-polyfill
в вашу сборку, а затем scheduler.yield()
можно использовать напрямую; полифил будет обрабатывать возврат к другим функциям планирования задач, поэтому он работает одинаково во всех браузерах.
В качестве альтернативы можно написать менее сложную версию в несколько строк, используя только setTimeout
завернутый в Promise, в качестве запасного варианта, если scheduler.yield()
недоступен.
function yieldToMain () {
if (globalThis.scheduler?.yield) {
return scheduler.yield();
}
// Fall back to yielding with setTimeout.
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
Хотя браузеры без поддержки scheduler.yield()
не получат приоритетного продолжения, они все равно будут уступать, чтобы браузер оставался отзывчивым.
Наконец, могут быть случаи, когда ваш код не может позволить себе перейти к основному потоку, если его продолжение не имеет приоритета (например, заведомо занятая страница, где передача рискует не завершить работу в течение некоторого времени). В этом случае scheduler.yield()
можно рассматривать как своего рода прогрессивное улучшение: выдайте в браузерах, где scheduler.yield()
доступен, в противном случае продолжайте.
Это можно сделать как путем обнаружения функций, так и путем возврата к ожиданию одной микрозадачи в удобной однострочной строке:
// Yield to the main thread if scheduler.yield() is available.
await globalThis.scheduler?.yield?.();
Прервите длительную работу с помощью scheduler.yield()
Преимущество использования любого из этих методов использования scheduler.yield()
заключается в том, что вы можете await
его в любой async
функции.
Например, если вам нужно выполнить массив заданий, которые часто в конечном итоге образуют длинную задачу, вы можете вставить выходные данные, чтобы разбить задачу.
async function runJobs(jobQueue) {
for (const job of jobQueue) {
// Run the job:
job();
// Yield to the main thread:
await yieldToMain();
}
}
Продолжение runJobs()
будет иметь приоритет, но по-прежнему позволит выполнять работу с более высоким приоритетом, например визуальное реагирование на ввод пользователя, без необходимости ждать завершения потенциально длинного списка заданий.
Однако это неэффективное использование доходности. scheduler.yield()
работает быстро и эффективно, но требует некоторых накладных расходов. Если некоторые задания в jobQueue
очень короткие, накладные расходы могут быстро привести к тому, что на выполнение и возобновление будет потрачено больше времени, чем на выполнение фактической работы.
Один из подходов — группировать задания, передавая результаты между ними только в том случае, если с момента последнего выполнения прошло достаточно времени. Общий срок составляет 50 миллисекунд, чтобы задачи не превращались в длинные задачи, но его можно настроить как компромисс между скоростью реагирования и временем завершения очереди заданий.
async function runJobs(jobQueue, deadline=50) {
let lastYield = performance.now();
for (const job of jobQueue) {
// Run the job:
job();
// If it's been longer than the deadline, yield to the main thread:
if (performance.now() - lastYield > deadline) {
await yieldToMain();
lastYield = performance.now();
}
}
}
В результате задания разбиваются, чтобы выполнение никогда не занимало слишком много времени, но исполнитель уступает основному потоку примерно каждые 50 миллисекунд.
Не используйте isInputPending()
API isInputPending()
предоставляет способ проверки того, пытался ли пользователь взаимодействовать со страницей, и уступает только в том случае, если ввод ожидает ответа.
Это позволяет JavaScript продолжать работу, если нет ожидающих входных данных, вместо того, чтобы уступить и оказаться в конце очереди задач. Это может привести к впечатляющему повышению производительности, как подробно описано в Intent to Ship , для сайтов, которые в противном случае не могли бы вернуться в основной поток.
Однако с момента запуска этого API наше понимание доходности возросло, особенно с появлением INP. Мы больше не рекомендуем использовать этот API , а вместо этого рекомендуем передавать данные независимо от того, ожидается ли ввод или нет, по ряду причин:
-
isInputPending()
может ошибочно возвращатьfalse
несмотря на то, что пользователь взаимодействовал в некоторых обстоятельствах. - Ввод — не единственный случай, когда задачи должны давать результат. Анимации и другие обычные обновления пользовательского интерфейса могут быть одинаково важны для предоставления адаптивной веб -страницы.
- С тех пор были введены более комплексные API -интерфейсы, которые обращаются к проблемам, таким как
scheduler.postTask()
иscheduler.yield()
.
Заключение
Управление задачами сложно, но это гарантирует, что ваша страница быстрее реагирует на взаимодействие с пользователями. Там нет единого совета по управлению и приоритетным задачам, а скорее ряд различных методов. Чтобы повторить, это основные вещи, которые вы захотите рассмотреть при управлении задачами:
- Получите основной поток для критических, пользовательских задач.
- Используйте
scheduler.yield()
- Наконец, сделайте как можно меньше работы в своих функциях.
scheduler.postTask()
узнать больше о scheduler.yield()
.
С помощью одного или нескольких из этих инструментов вы должны иметь возможность структурировать работу в вашем приложении, чтобы она приоритет потребностям пользователя, обеспечивая при этом, чтобы менее критическая работа была все еще выполнена. Это создаст лучший пользовательский опыт, который будет более отзывчивым и более приятным в использовании.
Особая благодарность Филиппу Уолтону за его техническое проверок этого руководства.
Миниатюрный образ, полученный из Unsplash , любезно предоставленной Амирали Мирхашемян .