No último módulo, apresentamos uma visão geral dos web workers. Os service workers podem melhorar a capacidade de resposta da entrada movendo o JavaScript da linha de execução principal para linhas de execução separadas do service worker, o que pode ajudar a melhorar a Interação com a próxima renderização (INP) do seu site quando você tem um trabalho que não precisa de acesso direto à linha de execução principal. No entanto, uma visão geral não é suficiente. Neste módulo, oferecemos um caso de uso concreto para um service worker.
Um caso de uso é um site que precisa remover metadados Exif de uma imagem. Esse não é um conceito tão absurdo. Na verdade, sites como o Flickr oferecem aos usuários uma maneira de ver os metadados Exif para saber detalhes técnicos sobre as imagens hospedadas, como profundidade de cor, marca e modelo da câmera e outros dados.
No entanto, a lógica para buscar uma imagem, convertê-la em um ArrayBuffer
e extrair os metadados Exif pode ser potencialmente cara se feita inteiramente na linha de execução principal. Felizmente, o escopo do service worker permite que esse trabalho seja feito
fora da linha de execução principal. Em seguida, usando o pipeline de mensagens do web worker, os metadados Exif são transmitidos de volta à linha de execução principal como uma string HTML e exibidos ao usuário.
Como é a linha de execução principal sem um service worker
Primeiro, observe como a linha de execução principal fica quando fazemos esse trabalho sem um web worker. Para fazer isso, siga estas etapas:
- Abra uma nova guia no Chrome e abra o DevTools.
- Abra o painel de performance.
- Acesse https://chrome.dev/learn-performance-exif-worker/without-worker.html.
- No painel de performance, clique em Gravar no canto superior direito do painel DevTools.
- Cole este link de imagem ou outro de sua escolha que contenha metadados Exif no campo e clique no botão Pegue esse JPEG!.
- Quando a interface for preenchida com metadados Exif, clique em Gravar novamente para parar a gravação.

Além de outras linhas de execução que podem estar presentes, como linhas de execução rasterizadoras e assim por diante, tudo no app ocorre na linha de execução principal. Na linha de execução principal, acontece o seguinte:
- O formulário recebe a entrada e envia uma solicitação
fetch
para receber a parte inicial da imagem que contém os metadados Exif. - Os dados da imagem são convertidos em um
ArrayBuffer
. - O script
exif-reader
é usado para extrair os metadados Exif da imagem. - Os metadados são extraídos para construir uma string HTML, que preenche o visualizador de metadados.
Agora, compare isso com uma implementação do mesmo comportamento, mas usando um service worker.
Como a linha de execução principal fica com um service worker
Agora que você viu como é extrair os metadados Exif de um arquivo JPEG na linha de execução principal, confira como fica quando um service worker está envolvido:
- Abra outra guia no Chrome e abra o DevTools dela.
- Abra o painel de performance.
- Acesse https://chrome.dev/learn-performance-exif-worker/with-worker.html.
- No painel de performance, clique no botão de gravação no canto superior direito do painel do DevTools.
- Cole este link de imagem no campo e clique no botão Pegue esse JPEG!.
- Quando a interface for preenchida com metadados Exif, clique no botão de gravação de novo para interromper a gravação.

Esse é o poder de um service worker. Em vez de fazer tudo na linha de execução principal, tudo, exceto o preenchimento do visualizador de metadados com HTML, é feito em uma linha de execução separada. Isso significa que a linha de execução principal fica livre para fazer outros trabalhos.
Talvez a maior vantagem seja que, ao contrário da versão do app que não usa um service worker, o script exif-reader
não é carregado na linha de execução principal, mas sim na linha de execução do service worker. Isso significa que o custo de
download, análise e compilação do script exif-reader
ocorre fora da
thread principal.
Agora vamos analisar o código do service worker que torna tudo isso possível.
Uma olhada no código do service worker
Não basta ver a diferença que um service worker faz. Também é útil entender, pelo menos neste caso, como é esse código para saber o que é possível no escopo do service worker.
Comece com o código da linha de execução principal que precisa ocorrer antes que o service worker possa entrar em cena:
// 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';
});
Esse código é executado na linha de execução principal e configura o formulário para enviar o URL da imagem ao
worker da Web. A partir daí, o código do service worker começa com uma instrução importScripts
que carrega o script exif-reader
externo e configura o
pipeline de mensagens para a linha de execução principal:
// 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);
});
});
Esse trecho de JavaScript configura o pipeline de mensagens para que, quando o usuário
envia o formulário com um URL para um arquivo JPEG, o URL chegue ao service worker.
Em seguida, este trecho de código extrai os metadados Exif do arquivo JPEG,
cria uma string HTML e envia esse HTML de volta ao window
para ser
mostrado ao usuário:
// 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('')
});
});
});
});
É um pouco longo, mas esse também é um caso de uso bastante complexo para web workers.
No entanto, os resultados valem a pena e não se limitam a esse caso de uso.
É possível usar web workers para todos os tipos de coisas, como isolar chamadas fetch
e processar respostas, processar grandes quantidades de dados sem bloquear a linha de execução principal. E isso é só o começo.
Ao melhorar a performance dos seus aplicativos da Web, comece pensando em tudo o que pode ser feito razoavelmente em um contexto de service worker. Os ganhos podem ser significativos e levar a uma experiência do usuário melhor no seu site.