Программы JavaScript запускаются в браузере в одном потоке и во время выполнения, например, Node.js. Когда код выполняется на вкладке браузера, все остальное останавливается: команды меню, загрузки, рендеринг, обновления DOM и даже анимация GIF.
Это редко очевидно для пользователя, потому что обработка происходит быстро небольшими порциями. Например: нажата кнопка, которая вызывает событие, которое запускает функцию, которая выполняет расчет и обновляет DOM. После завершения браузер может обрабатывать следующий элемент в очереди на обработку.
Код JavaScript не может ждать, чтобы что-то произошло; представьте себе разочарование, если приложение зависает каждый раз, когда оно делает запрос Ajax. Поэтому код JavaScript работает с использованием событий и обратных вызовов: процесс уровня браузера или операционной системы получает указание вызвать определенную функцию, когда операция завершена и результат готов.
В следующем примере функция обработчика выполняется, когда происходит событие нажатия кнопки, которое анимирует элемент путем применения класса CSS. Когда эта анимация завершается, анонимный обратный вызов удаляет класс:
// raise an event when a button is clicked
document.getElementById('clickme').addEventListener('click', handleClick);
// handle button click event
function handleClick(e) {
// get element to animate
let sprite = document.getElementById('sprite');
if (!sprite) return;
// remove 'animate' class when animation ends
sprite.addEventListener('animationend', () => {
sprite.classList.remove('animate');
});
// add 'animate' class
sprite.classList.add('animate');
}
ES2015 предоставил Promises
async
Для получения дополнительной информации см. « Управление потоком в современном JS ».
Блокирующие бандиты
К сожалению, некоторые операции JavaScript всегда будут синхронными, в том числе:
- текущие расчеты
- обновление DOM
- используя localStorage или IndexedDB для хранения и извлечения данных.
Следующее перо показывает захватчик, который использует комбинацию CSS-анимации для перемещения и JavaScript для перемещения конечностей. Изображение справа — это основной анимированный GIF. Нажмите кнопку записи с 100 000 await
Обновления DOM блокируются во время этой операции. Захватчик останавливается или заикается в большинстве браузеров. Анимированная GIF-анимация в некоторых приостановится. На более медленных устройствах может отображаться предупреждение «скрипт не отвечает» .
Это сложный пример, но он демонстрирует, как базовые операции могут повлиять на производительность интерфейса.
Веб-работники
Одним из решений для длительных процессов являются веб-работники . Это позволяет основному браузерному приложению запускать фоновый скрипт и обмениваться данными с использованием событий сообщений. Например:
sessionStorage operations
Скрипт веб-работника:
// main.js
// are web workers supported?
if (!window.Worker) return;
// start web worker script
let myWorker = new Worker('myworker.js');
// message received from myWorker
myWorker.onmessage = e => {
console.log('myworker sent:', e.data);
}
// send message to myWorker
myWorker.postMessage('hello');
Рабочий может даже порождать других рабочих для эмуляции сложных потоковых операций. Тем не менее, работники намеренно ограничены, и работник не может напрямую получить доступ к DOM или // myworker.js
(это фактически сделает многопоточный JavaScript-код и нарушит стабильность браузера.) Поэтому все сообщения отправляются в виде строк, что позволяет передавать объекты в кодировке JSON, но не DOM-узлы.
// start when a message is received
onmessage = e => {
console.log('myworker received:', e.data);
// ... long-running process ...
// post message back
postMessage('result');
};
Рабочие могут получить доступ к некоторым свойствам localStorage
В большинстве случаев рабочие используются для длительных вычислений — таких как трассировка лучей, обработка изображений, майнинг биткойнов и так далее.
(Node.js предлагает дочерние процессы, которые похожи на веб-работников, но имеют опции для запуска исполняемых файлов, написанных на других языках.)
Аппаратно-ускоренная анимация
Большинство современных браузеров не блокируют CSS-анимацию с аппаратным ускорением, которая запускается в их собственном слое.
По умолчанию, приведенный выше пример перемещает захватчик, изменяя window
Это и подобные свойства, такие как left-margin
left
Анимация более эффективна при использовании свойств width
transform
Они эффективно помещают элемент в отдельный слой композитинга, поэтому он может быть анимирован отдельно от графического процессора.
Установите флажок аппаратного ускорения, и анимация сразу станет более плавной. Теперь opacity
sessionStorage
захватчик продолжит движение, даже если анимированный GIF остановится. Обратите внимание, что движение конечности все равно будет приостановлено, потому что это контролируется JavaScript.
Хранение в памяти
Обновление объекта в памяти происходит значительно быстрее, чем при использовании механизма хранения, который записывает на диск. Выберите тип хранилища объектов на ручке и нажмите « написать» . Результаты могут отличаться, но они должны быть примерно в 10 раз быстрее, чем эквивалентная операция sessionStorage
Память изменчива: закрытие вкладки или навигация приводят к потере всех данных. Хорошим компромиссом является использование объектов в памяти для повышения производительности, а затем постоянное хранение данных в удобные моменты — например, когда страница выгружается:
// get previously-saved data
var store = JSON.parse(localStorage.getItem('store'));
// initialise an empty store
if (!store || !store.initialized) {
store = {
initialized: true,
username: 'anonymous'
score: 0,
best: { score: 1000, username: 'Alice' }
}
};
// save to localStorage on page unload
window.addEventListener('unload', () => {
localStorage.setItem('store', JSON.stringify(store));
});
Игры или одностраничные приложения могут потребовать более сложных опций. Например, данные сохраняются, когда:
- нет активности пользователя (события мыши, касания или клавиатуры) в течение нескольких секунд
- игра приостановлена или вкладка приложения находится в фоновом режиме (см. API видимости страницы )
- есть естественная пауза — например, когда игрок умирает, завершает уровень, перемещается между основными экранами и так далее.
Веб-производительность
Веб-производительность является горячей темой. Разработчики менее стеснены ограничениями браузера, а пользователи ожидают высокой производительности приложений, подобных ОС.
Выполняйте как можно реже как можно меньше обработки, и DOM никогда не будет заметно заблокирован. К счастью, есть варианты в ситуациях, когда нельзя избежать длительных задач.
Пользователи и клиенты могут никогда не заметить ваши оптимизации скорости, но они всегда будут жаловаться, когда приложение замедляется!