В последнем модуле был дан обзор веб-воркеров . Веб-работники могут улучшить скорость реагирования на ввод, переместив JavaScript из основного потока в отдельные потоки веб-работников, что может помочь улучшить взаимодействие вашего веб-сайта с следующей отрисовкой (INP), когда у вас есть работа, которая не требует прямого доступа к основному потоку. Однако одного обзора недостаточно, и в этом модуле предлагается конкретный вариант использования веб-работника.
Одним из таких вариантов использования может быть веб-сайт, которому необходимо удалить метаданные Exif из изображения — это не такая уж надуманная концепция. Фактически, такие веб-сайты, как Flickr, предлагают пользователям возможность просматривать метаданные Exif, чтобы узнать технические подробности о размещенных на них изображениях, такие как глубина цвета, марка и модель камеры и другие данные.
Однако логика получения изображения, его преобразования в ArrayBuffer
и извлечения метаданных Exif может быть потенциально дорогостоящей, если выполняться полностью в основном потоке. К счастью, область веб-воркера позволяет выполнять эту работу вне основного потока. Затем, используя конвейер обмена сообщениями веб-работника, метаданные Exif передаются обратно в основной поток в виде строки HTML и отображаются пользователю.
Как выглядит основной поток без веб-воркера
Во-первых, посмотрите, как выглядит основной поток, когда мы выполняем эту работу без веб-воркера. Для этого выполните следующие действия:
- Откройте новую вкладку в Chrome и откройте ее DevTools.
- Откройте панель производительности .
- Перейдите по адресу https://exif-worker.glitch.me/without-worker.html .
- На панели производительности нажмите «Запись» в правом верхнем углу панели DevTools.
- Вставьте ссылку на это изображение (или другое по вашему выбору, содержащее метаданные Exif) в поле и нажмите « Получить этот JPEG!». кнопка.
- Как только интерфейс заполнится метаданными Exif, снова нажмите «Запись» , чтобы остановить запись.
![Профилировщик производительности, показывающий активность приложения для извлечения метаданных изображений, происходящую полностью в основном потоке. Существует две существенные длинные задачи: одна выполняет выборку для получения запрошенного изображения и его декодирования, а другая извлекает метаданные из изображения.](https://web.dev/static/learn/performance/web-worker-demo/image/fig-1.opt.png?authuser=6&hl=ru)
Обратите внимание: за исключением других потоков, которые могут присутствовать, таких как потоки растеризации и т. д., все в приложении происходит в основном потоке. В основном потоке происходит следующее:
- Форма принимает входные данные и отправляет запрос
fetch
, чтобы получить начальную часть изображения, содержащую метаданные Exif. - Данные изображения преобразуются в
ArrayBuffer
. - Скрипт
exif-reader
используется для извлечения метаданных Exif из изображения. - Метаданные очищаются для создания строки HTML, которая затем заполняется средством просмотра метаданных.
Теперь сравните это с реализацией того же поведения, но с использованием веб-воркера!
Как выглядит основной поток у веб-воркера
Теперь, когда вы увидели, как выглядит извлечение метаданных Exif из файла JPEG в основном потоке, взгляните, как это выглядит, когда в этом участвует веб-воркер:
- Откройте другую вкладку в Chrome и откройте ее DevTools.
- Откройте панель производительности .
- Перейдите по адресу https://exif-worker.glitch.me/with-worker.html .
- На панели производительности нажмите кнопку записи в правом верхнем углу панели DevTools.
- Вставьте ссылку на это изображение в поле и нажмите « Получить этот JPEG!». кнопка.
- Как только интерфейс заполнится метаданными Exif, снова нажмите кнопку записи , чтобы остановить запись.
![Профилировщик производительности, показывающий активность приложения для извлечения метаданных изображений, происходящую как в основном потоке, так и в рабочем веб-потоке. Несмотря на то, что в основном потоке по-прежнему выполняются длинные задачи, они существенно короче: получение/декодирование изображений и извлечение метаданных полностью выполняются в рабочем веб-потоке. Единственная работа основного потока включает в себя передачу данных веб-воркеру и обратно.](https://web.dev/static/learn/performance/web-worker-demo/image/fig-2.opt.png?authuser=6&hl=ru)
В этом сила веб-работника. Вместо того, чтобы делать все в основном потоке, все, кроме заполнения средства просмотра метаданных HTML, выполняется в отдельном потоке. Это означает, что основной поток освобождается для выполнения другой работы.
Возможно, самым большим преимуществом здесь является то, что, в отличие от версии этого приложения, которая не использует веб-воркер, сценарий exif-reader
загружается не в основной поток, а в рабочий веб-поток. Это означает, что затраты на загрузку, анализ и компиляцию сценария exif-reader
выполняются вне основного потока.
Теперь углубимся в код веб-работника, который делает все это возможным!
Взгляните на код веб-работника
Недостаточно увидеть разницу, которую вносит веб-воркер, это также помогает понять — по крайней мере, в этом случае — как выглядит этот код, чтобы вы знали, что возможно в области веб-воркера.
Начните с кода основного потока, который должен произойти, прежде чем веб-воркер сможет ввести изображение:
// scripts.js
// Register the Exif reader web worker:
const exifWorker = new Worker('/js/with-worker/exif-worker.js');
// We have to send image requests through this proxy due to CORS limitations:
const imageFetchPrefix = 'https://res.cloudinary.com/demo/image/fetch/';
// Necessary elements we need to select:
const imageFetchPanel = document.getElementById('image-fetch');
const imageExifDataPanel = document.getElementById('image-exif-data');
const exifDataPanel = document.getElementById('exif-data');
const imageInput = document.getElementById('image-url');
// What to do when the form is submitted.
document.getElementById('image-form').addEventListener('submit', event => {
// Don't let the form submit by default:
event.preventDefault();
// Send the image URL to the web worker on submit:
exifWorker.postMessage(`${imageFetchPrefix}${imageInput.value}`);
});
// This listens for the Exif metadata to come back from the web worker:
exifWorker.addEventListener('message', ({ data }) => {
// This populates the Exif metadata viewer:
exifDataPanel.innerHTML = data.message;
imageFetchPanel.style.display = 'none';
imageExifDataPanel.style.display = 'block';
});
Этот код выполняется в основном потоке и настраивает форму для отправки URL-адреса изображения веб-воркеру. Далее код веб-воркера начинается с оператора importScripts
, который загружает внешний скрипт exif-reader
, а затем настраивает конвейер обмена сообщениями для основного потока:
// exif-worker.js
// Import the exif-reader script:
importScripts('/js/with-worker/exifreader.js');
// Set up a messaging pipeline to send the Exif data to the `window`:
self.addEventListener('message', ({ data }) => {
getExifDataFromImage(data).then(status => {
self.postMessage(status);
});
});
Этот фрагмент JavaScript настраивает конвейер обмена сообщениями так, что когда пользователь отправляет форму с URL-адресом в файл JPEG, URL-адрес поступает в веб-воркер. Далее следующий фрагмент кода извлекает метаданные Exif из файла JPEG, создает строку HTML и отправляет этот HTML обратно в window
, чтобы в конечном итоге отобразить его пользователю:
// Takes a blob to transform the image data into an `ArrayBuffer`:
// NOTE: these promises are simplified for readability, and don't include
// rejections on failures. Check out the complete web worker code:
// https://glitch.com/edit/#!/exif-worker?path=js%2Fwith-worker%2Fexif-worker.js%3A10%3A5
const readBlobAsArrayBuffer = blob => new Promise(resolve => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.readAsArrayBuffer(blob);
});
// Takes the Exif metadata and converts it to a markup string to
// display in the Exif metadata viewer in the DOM:
const exifToMarkup = exif => Object.entries(exif).map(([exifNode, exifData]) => {
return `
<details>
<summary>
<h2>${exifNode}</h2>
</summary>
<p>${exifNode === 'base64' ? `<img src="data:image/jpeg;base64,${exifData}">` : typeof exifData.value === 'undefined' ? exifData : exifData.description || exifData.value}</p>
</details>
`;
}).join('');
// Fetches a partial image and gets its Exif data
const getExifDataFromImage = imageUrl => new Promise(resolve => {
fetch(imageUrl, {
headers: {
// Use a range request to only download the first 64 KiB of an image.
// This ensures bandwidth isn't wasted by downloading what may be a huge
// JPEG file when all that's needed is the metadata.
'Range': `bytes=0-${2 ** 10 * 64}`
}
}).then(response => {
if (response.ok) {
return response.clone().blob();
}
}).then(responseBlob => {
readBlobAsArrayBuffer(responseBlob).then(arrayBuffer => {
const tags = ExifReader.load(arrayBuffer, {
expanded: true
});
resolve({
status: true,
message: Object.values(tags).map(tag => exifToMarkup(tag)).join('')
});
});
});
});
Это немного сложно читать, но это также довольно сложный вариант использования для веб-работников. Однако результаты стоят затраченных усилий, и они не ограничиваются этим вариантом использования. Вы можете использовать веб-воркеры для самых разных целей, например, для изоляции вызовов fetch
и обработки ответов, обработки больших объемов данных без блокировки основного потока — и это только для начала.
При повышении производительности ваших веб-приложений начните думать обо всем, что можно разумно сделать в контексте веб-работника. Выигрыш может быть значительным и может привести к общему улучшению пользовательского опыта вашего веб-сайта.
,В последнем модуле был дан обзор веб-воркеров . Веб-работники могут улучшить скорость реагирования на ввод, переместив JavaScript из основного потока в отдельные потоки веб-работников, что может помочь улучшить взаимодействие вашего веб-сайта с следующей отрисовкой (INP), когда у вас есть работа, которая не требует прямого доступа к основному потоку. Однако одного обзора недостаточно, и в этом модуле предлагается конкретный вариант использования веб-работника.
Одним из таких вариантов использования может быть веб-сайт, которому необходимо удалить метаданные Exif из изображения — это не такая уж надуманная концепция. Фактически, такие веб-сайты, как Flickr, предлагают пользователям возможность просматривать метаданные Exif, чтобы узнать технические подробности о размещаемых на них изображениях, такие как глубина цвета, марка и модель камеры, а также другие данные.
Однако логика получения изображения, его преобразования в ArrayBuffer
и извлечения метаданных Exif может быть потенциально дорогостоящей, если выполняться полностью в основном потоке. К счастью, область веб-воркера позволяет выполнять эту работу вне основного потока. Затем, используя конвейер обмена сообщениями веб-работника, метаданные Exif передаются обратно в основной поток в виде строки HTML и отображаются пользователю.
Как выглядит основной поток без веб-воркера
Во-первых, посмотрите, как выглядит основной поток, когда мы выполняем эту работу без веб-воркера. Для этого выполните следующие действия:
- Откройте новую вкладку в Chrome и откройте ее DevTools.
- Откройте панель производительности .
- Перейдите по адресу https://exif-worker.glitch.me/without-worker.html .
- На панели производительности нажмите «Запись» в правом верхнем углу панели DevTools.
- Вставьте ссылку на это изображение (или другое по вашему выбору, содержащее метаданные Exif) в поле и нажмите « Получить этот JPEG!». кнопка.
- Как только интерфейс заполнится метаданными Exif, снова нажмите «Запись» , чтобы остановить запись.
![Профилировщик производительности, показывающий активность приложения для извлечения метаданных изображений, происходящую полностью в основном потоке. Существует две существенные длинные задачи: одна выполняет выборку для получения запрошенного изображения и его декодирования, а другая извлекает метаданные из изображения.](https://web.dev/static/learn/performance/web-worker-demo/image/fig-1.opt.png?authuser=6&hl=ru)
Обратите внимание: за исключением других потоков, которые могут присутствовать, таких как потоки растеризации и т. д., все в приложении происходит в основном потоке. В основном потоке происходит следующее:
- Форма принимает входные данные и отправляет запрос
fetch
, чтобы получить начальную часть изображения, содержащую метаданные Exif. - Данные изображения преобразуются в
ArrayBuffer
. - Скрипт
exif-reader
используется для извлечения метаданных Exif из изображения. - Метаданные очищаются для создания строки HTML, которая затем заполняется средством просмотра метаданных.
Теперь сравните это с реализацией того же поведения, но с использованием веб-воркера!
Как выглядит основной поток у веб-воркера
Теперь, когда вы увидели, как выглядит извлечение метаданных Exif из файла JPEG в основном потоке, взгляните, как это выглядит, когда в этом участвует веб-воркер:
- Откройте другую вкладку в Chrome и откройте ее DevTools.
- Откройте панель производительности .
- Перейдите по адресу https://exif-worker.glitch.me/with-worker.html .
- На панели производительности нажмите кнопку записи в правом верхнем углу панели DevTools.
- Вставьте ссылку на это изображение в поле и нажмите « Получить этот JPEG!». кнопка.
- Как только интерфейс заполнится метаданными Exif, снова нажмите кнопку записи , чтобы остановить запись.
![Профилировщик производительности, показывающий активность приложения для извлечения метаданных изображений, происходящую как в основном потоке, так и в рабочем веб-потоке. Несмотря на то, что в основном потоке по-прежнему выполняются длинные задачи, они существенно короче: получение/декодирование изображений и извлечение метаданных полностью выполняются в рабочем веб-потоке. Единственная работа основного потока включает в себя передачу данных веб-воркеру и обратно.](https://web.dev/static/learn/performance/web-worker-demo/image/fig-2.opt.png?authuser=6&hl=ru)
В этом сила веб-работника. Вместо того, чтобы делать все в основном потоке, все, кроме заполнения средства просмотра метаданных HTML, выполняется в отдельном потоке. Это означает, что основной поток освобождается для выполнения другой работы.
Возможно, самым большим преимуществом здесь является то, что, в отличие от версии этого приложения, которая не использует веб-воркер, сценарий exif-reader
загружается не в основной поток, а в рабочий веб-поток. Это означает, что затраты на загрузку, анализ и компиляцию сценария exif-reader
выполняются вне основного потока.
Теперь углубимся в код веб-работника, который делает все это возможным!
Взгляните на код веб-работника
Недостаточно увидеть разницу, которую вносит веб-воркер, это также помогает понять — по крайней мере, в этом случае — как выглядит этот код, чтобы вы знали, что возможно в области веб-воркера.
Начните с кода основного потока, который должен произойти, прежде чем веб-воркер сможет ввести изображение:
// scripts.js
// Register the Exif reader web worker:
const exifWorker = new Worker('/js/with-worker/exif-worker.js');
// We have to send image requests through this proxy due to CORS limitations:
const imageFetchPrefix = 'https://res.cloudinary.com/demo/image/fetch/';
// Necessary elements we need to select:
const imageFetchPanel = document.getElementById('image-fetch');
const imageExifDataPanel = document.getElementById('image-exif-data');
const exifDataPanel = document.getElementById('exif-data');
const imageInput = document.getElementById('image-url');
// What to do when the form is submitted.
document.getElementById('image-form').addEventListener('submit', event => {
// Don't let the form submit by default:
event.preventDefault();
// Send the image URL to the web worker on submit:
exifWorker.postMessage(`${imageFetchPrefix}${imageInput.value}`);
});
// This listens for the Exif metadata to come back from the web worker:
exifWorker.addEventListener('message', ({ data }) => {
// This populates the Exif metadata viewer:
exifDataPanel.innerHTML = data.message;
imageFetchPanel.style.display = 'none';
imageExifDataPanel.style.display = 'block';
});
Этот код выполняется в основном потоке и настраивает форму для отправки URL-адреса изображения веб-воркеру. Далее код веб-воркера начинается с оператора importScripts
, который загружает внешний скрипт exif-reader
, а затем настраивает конвейер обмена сообщениями для основного потока:
// exif-worker.js
// Import the exif-reader script:
importScripts('/js/with-worker/exifreader.js');
// Set up a messaging pipeline to send the Exif data to the `window`:
self.addEventListener('message', ({ data }) => {
getExifDataFromImage(data).then(status => {
self.postMessage(status);
});
});
Этот фрагмент JavaScript настраивает конвейер обмена сообщениями так, что когда пользователь отправляет форму с URL-адресом в файл JPEG, URL-адрес поступает в веб-воркер. Далее следующий фрагмент кода извлекает метаданные Exif из файла JPEG, создает строку HTML и отправляет этот HTML обратно в window
, чтобы в конечном итоге отобразить его пользователю:
// Takes a blob to transform the image data into an `ArrayBuffer`:
// NOTE: these promises are simplified for readability, and don't include
// rejections on failures. Check out the complete web worker code:
// https://glitch.com/edit/#!/exif-worker?path=js%2Fwith-worker%2Fexif-worker.js%3A10%3A5
const readBlobAsArrayBuffer = blob => new Promise(resolve => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.readAsArrayBuffer(blob);
});
// Takes the Exif metadata and converts it to a markup string to
// display in the Exif metadata viewer in the DOM:
const exifToMarkup = exif => Object.entries(exif).map(([exifNode, exifData]) => {
return `
<details>
<summary>
<h2>${exifNode}</h2>
</summary>
<p>${exifNode === 'base64' ? `<img src="data:image/jpeg;base64,${exifData}">` : typeof exifData.value === 'undefined' ? exifData : exifData.description || exifData.value}</p>
</details>
`;
}).join('');
// Fetches a partial image and gets its Exif data
const getExifDataFromImage = imageUrl => new Promise(resolve => {
fetch(imageUrl, {
headers: {
// Use a range request to only download the first 64 KiB of an image.
// This ensures bandwidth isn't wasted by downloading what may be a huge
// JPEG file when all that's needed is the metadata.
'Range': `bytes=0-${2 ** 10 * 64}`
}
}).then(response => {
if (response.ok) {
return response.clone().blob();
}
}).then(responseBlob => {
readBlobAsArrayBuffer(responseBlob).then(arrayBuffer => {
const tags = ExifReader.load(arrayBuffer, {
expanded: true
});
resolve({
status: true,
message: Object.values(tags).map(tag => exifToMarkup(tag)).join('')
});
});
});
});
Это немного сложно читать, но это также довольно сложный вариант использования для веб-работников. Однако результаты стоят затраченных усилий, и они не ограничиваются этим вариантом использования. Вы можете использовать веб-воркеры для самых разных целей, например, для изоляции вызовов fetch
и обработки ответов, обработки больших объемов данных без блокировки основного потока — и это только для начала.
При повышении производительности ваших веб-приложений начните думать обо всем, что можно разумно сделать в контексте веб-работника. Выигрыш может быть значительным и может привести к общему улучшению пользовательского опыта вашего веб-сайта.