Nel modulo precedente, è stata fornita una panoramica dei web worker. I web worker possono migliorare la reattività all'input spostando JavaScript dal thread principale in thread dei web worker separati, il che può contribuire a migliorare l'interazione con il Next Paint (INP) del tuo sito web quando hai un lavoro che non richiede l'accesso diretto al thread principale. Tuttavia, una panoramica da sola non è sufficiente e, in questo modulo, viene offerto un caso d'uso concreto per un web worker.
Uno di questi casi d'uso potrebbe essere un sito web che deve rimuovere i metadati Exif da un'immagine. Questo non è un concetto così inverosimile. Infatti, siti web come Flickr offrono agli utenti un modo per visualizzare i metadati EXIF per conoscere dettagli tecnici sulle immagini ospitate, come la profondità di colore, la marca e il modello della fotocamera e altri dati.
Tuttavia, la logica per il recupero di un'immagine, la sua conversione in un file ArrayBuffer
e l'estrazione dei metadati Exif potrebbe essere potenzialmente costosa se eseguita interamente sul thread principale. Fortunatamente, l'ambito del web worker consente di svolgere
questo lavoro dal thread principale. Quindi, utilizzando la pipeline di messaggistica del web worker, i metadati Exif vengono trasmessi al thread principale come stringa HTML e mostrati all'utente.
L'aspetto del thread principale senza un web worker
Innanzitutto, osserva l'aspetto del thread principale quando lavoriamo senza un web worker. Per eseguire questa operazione, procedi nel seguente modo:
- Apri una nuova scheda in Chrome e apri i relativi DevTools.
- Apri il riquadro sul rendimento.
- Passa a https://exif-worker.glitch.me/without-worker.html.
- Nel riquadro delle prestazioni, fai clic su Record (Registra) nell'angolo in alto a destra del riquadro DevTools.
- Incolla nel campo questo link immagine o un'altra a tua scelta contenente metadati EXIF e fai clic sul pulsante Scarica JPEG.
- Una volta completata l'interfaccia con i metadati EXIF, fai di nuovo clic su Record per interrompere la registrazione.
Tieni presente che, a parte altri thread che potrebbero essere presenti, come i thread dell'unità di rasterizzazione e così via, tutto ciò che si trova nell'app avviene sul thread principale. Nel thread principale, avviene quanto segue:
- Il modulo prende l'input e invia una richiesta
fetch
per ottenere la porzione iniziale dell'immagine contenente i metadati EXIF. - I dati dell'immagine vengono convertiti in un
ArrayBuffer
. - Lo script
exif-reader
viene utilizzato per estrarre i metadati EXIF dall'immagine. - I metadati vengono estratti per creare una stringa HTML che a sua volta compila il visualizzatore metadati.
Ora però, con l'implementazione dello stesso comportamento, ma usando un web worker.
Come si presenta il thread principale con un web worker
Ora che hai visto come si possono estrarre i metadati EXIF da un file JPEG sul thread principale, dai un'occhiata all'aspetto di un web worker:
- Apri un'altra scheda in Chrome e apri i relativi DevTools.
- Apri il riquadro sul rendimento.
- Vai a https://exif-worker.glitch.me/with-worker.html.
- Nel riquadro delle prestazioni, fai clic sul pulsante Registra nell'angolo in alto a destra del riquadro DevTools.
- Incolla questa immagine link nel campo e fai clic sul pulsante Ottieni quel JPEG.
- Una volta che l'interfaccia viene compilata con i metadati EXIF, fai di nuovo clic sul pulsante Registra per interrompere la registrazione.
Questa è la forza di un web worker. Anziché eseguire tutte le operazioni nel thread principale, tutto tranne il completamento di HTML nel visualizzatore di metadati, viene eseguita in un thread separato. Ciò significa che il thread principale viene liberato per svolgere altre operazioni.
Forse il vantaggio più grande in questo caso è che, a differenza della versione di questa app che
non utilizza un web worker, lo script exif-reader
non viene caricato nel thread
principale, ma piuttosto nel thread del worker web. Ciò significa che i costi di download, analisi e compilazione dello script exif-reader
vengono effettuati al di fuori del thread principale.
Ora passiamo ad approfondire il codice dei web worker che rende tutto questo possibile.
Uno sguardo al codice del web worker
Non è sufficiente vedere la differenza che fa un web worker, è anche utile capire, almeno in questo caso, l'aspetto di quel codice in modo da sapere cosa è possibile nell'ambito del web worker.
Inizia con il codice del thread principale che deve verificarsi prima che il web worker possa accedere all'immagine:
// 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';
});
Questo codice viene eseguito sul thread principale e configura il modulo per inviare l'URL dell'immagine al web worker. Da qui, il codice del web worker inizia con un'istruzione importScripts
che carica lo script exif-reader
esterno, quindi configura la pipeline di messaggistica sul thread principale:
// 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);
});
});
Questo bit di JavaScript configura la pipeline di messaggistica in modo che, quando l'utente invia il modulo con un URL che rimanda a un file JPEG, che l'URL arrivi al web worker.
Da qui, questo bit di codice successivo estrae i metadati EXIF dal file JPEG, crea una stringa HTML e invia il codice HTML a window
per essere poi mostrato all'utente:
// 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('')
});
});
});
});
È un po' da leggere, ma è anche un caso d'uso piuttosto coinvolto per i web worker.
Tuttavia, i risultati valgono il lavoro e non solo in questo caso d'uso.
Puoi utilizzare i web worker per qualsiasi cosa, ad esempio per isolare le chiamate fetch
ed elaborare le risposte, elaborare grandi quantità di dati senza bloccare il thread principale. Questa pratica è solo per i principianti.
Quando migliori le prestazioni delle tue applicazioni web, inizia a pensare a qualsiasi cosa che possa essere ragionevolmente effettuata nel contesto di un web worker. I guadagni potrebbero essere significativi e possono portare a una migliore esperienza utente complessiva sul tuo sito web.