No último módulo, apresentamos uma visão geral dos web workers. Os web workers podem melhorar a capacidade de resposta de entrada movendo o JavaScript da linha de execução principal para sequências de execução separadas do web worker, o que pode ajudar a melhorar a interação do seu site ao Next Paint (INP) quando há um trabalho que não precisa de acesso direto ao linha de execução principal. No entanto, uma visão geral por si só não é suficiente, e, neste módulo, é oferecido um caso de uso concreto para um funcionário da Web.
Um desses casos de uso pode ser um site que precisa remover metadados Exif de uma imagem. Esse conceito não é tão difícil de descobrir. Na verdade, sites como o Flickr oferecem aos usuários uma maneira de visualizar os metadados Exif para aprender detalhes técnicos sobre o imagens que hospedam, como profundidade de cor, marca e modelo da câmera e outros dados.
No entanto, a lógica para buscar uma imagem e convertê-la em um ArrayBuffer
e a extração dos metadados Exif pode ser potencialmente cara se feita totalmente
na linha de execução principal. Felizmente, o escopo do web worker permite que esse trabalho seja feito
fora da linha de execução principal. Em seguida, usando o pipeline de mensagens do worker da Web,
Os metadados EXIF são transmitidos de volta para a linha de execução principal como uma string HTML.
exibidos ao usuário.
Como é a linha de execução principal sem um worker da Web
Primeiro, observe como fica a linha de execução principal quando fazemos esse trabalho sem uma worker da Web. Para fazer isso, siga estas etapas:
- Abra uma nova guia no Chrome e acesse as DevTools.
- Abra o painel de desempenho.
- Acesse https://exif-worker.glitch.me/without-worker.html.
- No painel de desempenho, clique em Record no canto superior direito do no painel DevTools.
- Cole este link de imagem ou outro de sua escolha que contenha Exif metadados no campo e clique no botão Get that JPEG!.
- Quando a interface for preenchida com metadados Exif, clique novamente em Record para pare a gravação.
Exceto por outros threads que podem estar presentes, como um rasterizador linhas de execução e assim por diante, tudo no app ocorre na linha de execução principal. Na rede principal encadeamento, acontece o seguinte:
- O formulário recebe a entrada e envia uma solicitação
fetch
para conseguir o da imagem que contém os metadados Exif. - Os dados da imagem são convertidos em
ArrayBuffer
. - O script
exif-reader
(link em inglês) é usado para extrair os metadados EXIF do imagem. - Os metadados são raspados para construir uma string HTML, que preenche o visualizador de metadados.
Compare isso com uma implementação do mesmo comportamento, mas usando uma interface trabalhador!
Como é a linha de execução principal com um worker da Web.
Agora que você sabe como extrair os metadados Exif de uma JPEG no encadeamento principal, dê uma olhada em como fica quando um servidor worker está na combinação:
- Abra outra guia no Chrome e abra as DevTools.
- Abra o painel de desempenho.
- Acesse https://exif-worker.glitch.me/with-worker.html.
- No painel de desempenho, clique no botão de gravação no canto superior direito. do painel do DevTools.
- Cole este link da imagem no campo e clique no botão Obter esse JPEG!.
- Depois que a interface for preenchida com metadados Exif, clique no botão de registro. novamente para interromper a gravação.
Esse é o poder de um web worker. Em vez de fazer tudo no , tudo menos o preenchimento do visualizador de metadados com HTML é feito em um em uma linha de execução separada. Isso significa que a linha de execução principal fica liberada para outras tarefas.
Talvez a maior vantagem aqui seja que, ao contrário da versão desse aplicativo que
não usar um worker da Web, o script exif-reader
não será carregado na interface principal
linha de execução, mas na linha de execução do web worker. Isso significa que o custo
o download, a análise e a compilação do script exif-reader
ocorrem fora da
linha de execução principal.
Agora vamos analisar o código do web worker que torna tudo isso possível.
Uma análise do código do web worker
Não basta ver a diferença que um web worker faz, mas também ajudar a entender, pelo menos neste caso, como é o código para que você saiba o que possível no escopo do web worker.
Comece com o código da linha de execução principal que precisa ocorrer antes que o worker da Web possa insira a imagem:
// 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 à
o web worker. A partir daí, o código do worker da Web começa com uma importScripts
.
que carrega o script exif-reader
externo e configura a
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 bit 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 chega no web worker.
A partir daí, o próximo 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
exibido 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://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('')
});
});
});
});
É um pouco de leitura, mas esse também é um caso de uso bastante complexo para web workers.
No entanto, os resultados valem o trabalho e não se limitam apenas a este caso de uso.
Você pode usar os web workers para todo tipo de coisa, como isolar chamadas fetch
e processamento de respostas, processamento de grandes volumes de dados sem bloquear
linha de execução principal, e isso é só para começar.
Ao melhorar o desempenho de seus aplicativos da Web, comece a pensar qualquer coisa que possa ser razoavelmente feita em um contexto de web worker. Os ganhos podem ser significativa e pode resultar em uma melhor experiência do usuário em seu site.