В последнем модуле был дан обзор веб-воркеров . Веб-воркеры могут улучшить отзывчивость ввода, перемещая JavaScript из основного потока в отдельные потоки веб-воркеров, что может помочь улучшить взаимодействие вашего веб-сайта с Next Paint (INP) , когда у вас есть работа, не требующая прямого доступа к основному потоку. Однако одного обзора недостаточно, и в этом модуле предлагается конкретный вариант использования веб-воркера.
Одним из таких вариантов использования может быть веб-сайт, которому необходимо удалить метаданные Exif из изображения — это не такая уж и надуманная концепция. Фактически, такие веб-сайты, как Flickr, предлагают пользователям способ просмотра метаданных Exif, чтобы узнать технические подробности об изображениях, которые они размещают, такие как глубина цвета, марка и модель камеры и другие данные.
Однако логика извлечения изображения, преобразования его в ArrayBuffer
и извлечения метаданных Exif может быть потенциально затратной, если выполнять ее полностью в основном потоке. К счастью, область действия веб-воркера позволяет выполнять эту работу вне основного потока. Затем, используя конвейер сообщений веб-воркера, метаданные Exif передаются обратно в основной поток в виде строки HTML и отображаются для пользователя.
Как выглядит основной поток без веб-воркера
Сначала посмотрите, как выглядит основной поток, когда мы делаем эту работу без веб-воркера. Для этого выполните следующие шаги:
- Откройте новую вкладку в Chrome и откройте ее DevTools.
- Откройте панель производительности .
- Перейдите по адресу https://chrome.dev/learn-performance-exif-worker/without-worker.html .
- На панели производительности нажмите кнопку «Запись» в правом верхнем углу панели DevTools.
- Вставьте эту ссылку на изображение (или другую ссылку на изображение по вашему выбору, содержащую метаданные Exif) в поле и нажмите кнопку «Получить JPEG!» .
- После того как интерфейс заполнится метаданными Exif, нажмите кнопку «Запись» еще раз, чтобы остановить запись.

Обратите внимание, что — помимо других потоков, которые могут присутствовать, таких как потоки растеризатора и т. д. — все в приложении происходит в основном потоке. В основном потоке происходит следующее:
- Форма принимает входные данные и отправляет запрос
fetch
для получения начальной части изображения, содержащей метаданные Exif. - Данные изображения преобразуются в
ArrayBuffer
. - Скрипт
exif-reader
используется для извлечения метаданных Exif из изображения. - Метаданные извлекаются для создания HTML-строки, которая затем заполняет средство просмотра метаданных.
А теперь сравните это с реализацией того же поведения, но с использованием веб-воркера!
Как выглядит основной поток с веб-воркером
Теперь, когда вы увидели, как выглядит извлечение метаданных Exif из файла JPEG в основном потоке, взгляните, как это выглядит, когда в процесс вовлечен веб-воркер:
- Откройте другую вкладку в Chrome и откройте ее DevTools.
- Откройте панель производительности .
- Перейдите по адресу https://chrome.dev/learn-performance-exif-worker/with-worker.html .
- На панели производительности нажмите кнопку записи в правом верхнем углу панели DevTools.
- Вставьте ссылку на это изображение в поле и нажмите кнопку «Получить JPEG!» .
- После того, как интерфейс заполнится метаданными Exif, нажмите кнопку записи еще раз, чтобы остановить запись.

Это сила веб-работника. Вместо того, чтобы делать все в основном потоке, все, кроме заполнения средства просмотра метаданных 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://chrome.dev/learn-performance-exif-worker/js/with-worker/exif-worker.js
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
и обработка ответов, обработка больших объемов данных без блокировки основного потока — и это только для начала.
При улучшении производительности ваших веб-приложений начните думать о том, что можно разумно сделать в контексте веб-работника. Выгоды могут быть значительными и могут привести к общему улучшению пользовательского опыта для вашего веб-сайта.