Конкретный вариант использования веб-работника

В последнем модуле был дан обзор веб-воркеров . Веб-работники могут улучшить скорость реагирования на ввод, переместив JavaScript из основного потока в отдельные потоки веб-работников, что может помочь улучшить взаимодействие вашего веб-сайта с следующей отрисовкой (INP), когда у вас есть работа, которая не требует прямого доступа к основному потоку. Однако одного обзора недостаточно, и в этом модуле предлагается конкретный вариант использования веб-работника.

Одним из таких вариантов использования может быть веб-сайт, которому необходимо удалить метаданные Exif из изображения — это не такая уж надуманная концепция. Фактически, такие веб-сайты, как Flickr, предлагают пользователям возможность просматривать метаданные Exif, чтобы узнать технические подробности о размещаемых на них изображениях, такие как глубина цвета, марка и модель камеры, а также другие данные.

Однако логика получения изображения, его преобразования в ArrayBuffer и извлечения метаданных Exif может быть потенциально дорогостоящей, если выполняться полностью в основном потоке. К счастью, область веб-воркера позволяет выполнять эту работу вне основного потока. Затем, используя конвейер обмена сообщениями веб-работника, метаданные Exif передаются обратно в основной поток в виде строки HTML и отображаются пользователю.

Как выглядит основной поток без веб-воркера

Во-первых, посмотрите, как выглядит основной поток, когда мы выполняем эту работу без веб-воркера. Для этого выполните следующие действия:

  1. Откройте новую вкладку в Chrome и откройте ее DevTools.
  2. Откройте панель производительности .
  3. Перейдите по адресу https://exif-worker.glitch.me/without-worker.html .
  4. На панели производительности нажмите «Запись» в правом верхнем углу панели DevTools.
  5. Вставьте ссылку на это изображение (или другое по вашему выбору, содержащее метаданные Exif) в поле и нажмите кнопку «Получить этот JPEG!». кнопка.
  6. Как только интерфейс заполнится метаданными Exif, снова нажмите «Запись» , чтобы остановить запись.
Профилировщик производительности, показывающий активность приложения для извлечения метаданных изображений, происходящую полностью в основном потоке. Существует две существенные длинные задачи: одна выполняет выборку для получения запрошенного изображения и его декодирования, а другая извлекает метаданные из изображения.
Активность основного потока в приложении для извлечения метаданных изображений. Обратите внимание, что все действия происходят в основном потоке.

Обратите внимание, что — за исключением других потоков, которые могут присутствовать, таких как потоки растеризации и т. д. — все в приложении происходит в основном потоке. В основном потоке происходит следующее:

  1. Форма принимает входные данные и отправляет запрос fetch чтобы получить начальную часть изображения, содержащую метаданные Exif.
  2. Данные изображения преобразуются в ArrayBuffer .
  3. Скрипт exif-reader используется для извлечения метаданных Exif из изображения.
  4. Метаданные очищаются для создания строки HTML, которая затем заполняется средством просмотра метаданных.

Теперь сравните это с реализацией того же поведения, но с использованием веб-воркера!

Как выглядит основной поток у веб-воркера

Теперь, когда вы увидели, как выглядит извлечение метаданных Exif из файла JPEG в основном потоке, взгляните, как это выглядит, когда в этом участвует веб-воркер:

  1. Откройте другую вкладку в Chrome и откройте ее DevTools.
  2. Откройте панель производительности .
  3. Перейдите по адресу https://exif-worker.glitch.me/with-worker.html .
  4. На панели производительности нажмите кнопку записи в правом верхнем углу панели DevTools.
  5. Вставьте ссылку на это изображение в поле и нажмите « Получить этот JPEG!». кнопка.
  6. Как только интерфейс заполнится метаданными 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://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 и обработки ответов, обработки больших объемов данных без блокировки основного потока — и это только для начала.

При повышении производительности ваших веб-приложений начните думать обо всем, что можно разумно сделать в контексте веб-работника. Выигрыш может быть значительным и может привести к общему улучшению пользовательского опыта вашего веб-сайта.