Наше положение, как владельцев популярного веб-сайта — и наша работа в поддержке популярной платформы React — дают нам уникальные возможности и понимание работы с браузером, которые мы хотим использовать для решения проблемы "очередей". Являясь активным участником сообщества веб-стандартов(W3C), мы принимали участие в обсуждении многих инноваций, включая service workers и CSS-overscroll, но до недавнего времени мы никогда не создавали собственные инициативы для улучшения веб-браузера. С целью добиться значительных увеличения производительности, был предложен новый API, который в результате тесного сотрудничества с нашими коллегами из Google Chrome, был предоставлен для ознакомительной версии. Chrome v74 будет включать в себя isInputPending API, и может быть использован для улучшения как общего времени выполнения JavaScript, так и времени отклика на события. Это лишь первый шаг к улучшению планирования JavaScript в вебе. Мы надеемся получить отзывы разработчиков и использовать их для создания финальной версии API.
Одним из важнейших показателей производительности в современном вебе является время, необходимое для ответа на событие пользователя (нажатие кнопки или ввод в поле), с его полной визуализацией. В Facebook мы разделяем и измеряем события в четыре этапа:
- операционная система получает данные
- фактическое начало обработки
- начало отображения изменений на экране в ответ на событие
- обработка завершена и результат визуализирован
Когда мы оценивали наши самые производительные продукты, было замечено, что время ожидания в очереди дает самые большие задержки. Под очередью имеется ввиду время между тем, когда пользователь взаимодействует со страницей (например, клик или тап), и когда начинается фактическая обработка события. В отдельных случаях эта задержка может быть достаточно весомой. Представьте, что вы щелкаете по значку уведомлений, а затем ждете минуту до реакции кнопки. Вероятно, никто не дождется ответа.
Быстрая загрузка или быстрое реагирование: выберите один
Существует сложный компромисс между быстрой загрузкой страницы и интерактивностью. Если веб-сайт требует JavaScript, один из вариантов состоит в том, чтобы запустить все это в одном блоке. Но это может создать проблемы. Механизмы JavaScript внутри веб-браузеров, как правило, являются однопоточными, то есть они могут одновременно выполнять только одну операцию на странице. В случае загрузки страницы это означает, что если пользователь нажимает на что-то, браузер должен ставить событие click в очередь, пока не будет запущен весь блок JavaScript.
Как и многие другие сайты, мы решаем эту проблему, разбивая JavaScript на мелкие блоки. Пока страница загружается, мы запускаем немного JavaScript, а затем возвращаем управление браузеру. Затем браузер может проверить свою очередь входных событий и посмотреть, есть ли что-то для обработки. Затем браузер может вернуться к выполнению блоков JavaScript по мере их добавления. Это помогает, но может вызвать другие проблемы. Каждый раз, когда мы возвращаем управление браузеру, ему требуется некоторое время, чтобы проверить свою очередь входных событий, обработать события и выбрать следующий блок JavaScript. Таким образом, хоть браузер реагирует на события быстрее, нам все же необходимо найти баланс между размерами блоков кода и частотой, с которой мы уступаем браузеру. Если мы меняем управление слишком часто, страница загружается слишком медленно, если наоборот — реже, браузеру требуется больше времени, чтобы реагировать на пользовательские события, и люди разочаровываются.
Если мы запускаем большие блоки JavaScript, браузер может отправлять пользовательские события с большой задержкой (вверху); если мы запускаем меньшие блоки, страница загружается дольше (снизу).
isInputPending решение
Когда мы впервые обнаружили задержки в очереди, мы обратились к нашим коллегам в Chrome. Мы хотели посмотреть, как все будет выглядеть, если мы придумаем новый подход к загрузке, который устранит эту дилемму компромисса. Поговорив с ними, мы предложили isInputPending. API isInputPending является первым использующим концепцию прерываний для пользовательских событий в вебе.
Под капотом isInputPending слушает входную очередь Chrome на стороне компилятора, чтобы перехватывать события, прежде чем они будут добавлены в главный поток. Поскольку это прослушивание выполняется вне основного потока, вызовы isInputPending не используют много вычислительных ресурсов и должны быть очень быстрыми. Это позволяет разработчикам часто вызывать API и максимизировать отзывчивость.
Как только мы подготовили это предложение, мы обратились к рабочей группе W3C по веб-производительности и получили согласие различных поставщиков браузеров, что наша идея заслуживает изучения. В дальнейшем, мы сотрудничали с нашими коллегами в Chrome, самостоятельно внедрили новый API и отправили соответствующие исправления кода в Chrome. Благодаря инженерам из Chrome, мы получили патчи пробной версии, которые позволяют тестировать изменения и получать отзывы от разработчиков перед полноценным релизом. Эта версия позволит нам понять, насколько важен этот API для разработчиков и будет определять наши будущие разговоры об этом API с поставщиками веб-браузеров. Это впервые, когда мы прошли все этапы разработки веб-API, от обсуждения предложения на форуме до передачи кода в веб-браузер.
Как работает isInputPending
Следуя из названия, isInputPending сообщает, есть ли события ожидающие ввода. Разработчики могут использовать эту информацию во время работы JavaScript, чтобы решить, хотят ли они вернуть контроль браузеру. При правильном использовании isInputPending может полностью устранить дилемму быстрой загрузки и интерактивности.
Для работы с API используется navigator.scheduling.isInputPending()
. По сути, если браузер ожидает, что событие будет отправлено, этот метод возвращает true
. При вызове без каких-либо аргументов проверяются все поддерживаемые типы событий. Кроме того, возможно вручную указать список типов событий: mouse, wheel, touch, которые следует проверять на ожидающий ввод.
Пример: проверка любых типов событий
while (workQueue.length > 0) {
if (navigator.scheduling.isInputPending()) {
// прервать работу для обработки входных событий
break;
}
let job = workQueue.shift();
job.execute();
}
Пример: проверка на конкретные входные события
while (workQueue.length > 0) {
if (navigator.scheduling.isInputPending(['mousedown', 'mouseup', 'keydown', 'keyup'])) {
// прервать работу для обработки события типа 'mousedown' или 'mouseup' или 'keydown' или 'keyup'
break;
}
let job = workQueue.shift();
job.execute();
}
Что дальше?
Если отзыв комьюнити будет положительным, isInputPending может стать полностью доступным в Chrome. Тогда мы сможем избавиться от этих заметных задержек в очереди и сделать работу в Интернете более быстрой и гибкой для людей на наших сайтах. Для разработчиков, которые также хотят избавиться от задержек в очередях и улучшить взаимодействие и производительность загрузки, пробная версия будет доступна в ближайшее время. Зарегистрируйтесь здесь и поделитесь своим мнением о пробной версии, как только она будет доступна.
Процесс переноса isInputPending в Chrome представляет собой новый метод разработки веб-стандартов в Facebook. Мы надеемся продолжить разработку новых API и увеличить свой вклад в веб-браузеры с открытым исходным кодом. В будущем мы могли бы потенциально встроить этот API непосредственно в React, чтобы разработчики могли получить преимущества API из коробки. Кроме того, isInputPending теперь является частью больших трудов по созданию примитивов для планирования в вебе. Мы с нетерпением ждем продолжения нашего сотрудничества с Chrome. В конце концов, мы надеемся увидеть инструменты браузера, которые позволят разработчикам глубже интегрироваться в очередь задач браузера и даже позволят разработчикам понять приоритеты браузера по различным сетевым запросам и задачам.
Примечание автора
Чтобы попробовать новое API нужно:
- установить браузер Google Chrome 74-76 версии
- включить флаг Experimental Web Platform features
Sirion
Чудесная возможность, но я не очень понял, как ей пользоваться на практике. Пример кода сделал бы статью прекраснее.