Kasus penggunaan pekerja web konkret

Dalam modul terakhir, telah diberikan ringkasan pekerja web. Pekerja web dapat meningkatkan responsivitas input dengan memindahkan JavaScript dari thread utama untuk thread pekerja web terpisah, yang dapat membantu meningkatkan Interaksi situs Anda ke Next Paint (INP) jika Anda memiliki pekerjaan yang tidak memerlukan akses langsung ke thread utama. Namun, ringkasan saja tidaklah cukup, dan dalam modul ini, kasus penggunaan konkret untuk pekerja web ditawarkan.

Salah satu kasus penggunaan tersebut adalah situs web yang perlu menghapus metadata Exif dari gambar — ini bukan konsep yang tidak masuk akal. Faktanya, situs web seperti Flickr menawarkan cara melihat metadata Exif untuk mempelajari detail teknis tentang gambar yang dihosting, seperti kedalaman warna, merek dan model kamera, serta layanan otomatis dan data skalabel.

Namun, logika untuk mengambil gambar dengan mengonversinya menjadi ArrayBuffer, dan mengekstrak {i>metadata<i} Exif bisa berpotensi mahal jika dilakukan sepenuhnya di thread utama. Untungnya, ruang lingkup pekerja web memungkinkan pekerjaan ini dilakukan keluar dari thread utama. Kemudian, dengan menggunakan pipeline pesan pekerja web, Metadata Exif dikirimkan kembali ke thread utama sebagai string HTML, dan yang ditampilkan kepada pengguna.

Seperti apa thread utama tanpa pekerja web

Pertama, amati tampilan thread utama saat kita melakukan pekerjaan ini tanpa pekerja web. Caranya, lakukan langkah-langkah berikut:

  1. Buka tab baru di Chrome, dan buka DevTools.
  2. Buka panel performa.
  3. Buka https://exif-worker.glitch.me/without-worker.html.
  4. Di panel performa, klik Rekam di sudut kanan atas panel DevTools.
  5. Tempel link gambar ini—atau link lain pilihan Anda yang berisi Exif metadata—di kolom, lalu klik tombol Get that JPEG!.
  6. Setelah antarmuka diisi dengan metadata Exif, klik Record lagi untuk berhenti merekam.
Performance profiler menampilkan aktivitas aplikasi ekstraktor metadata gambar yang terjadi sepenuhnya di thread utama. Ada dua tugas panjang yang substansial, yaitu menjalankan pengambilan untuk mendapatkan gambar yang diminta dan mendekodenya, dan tugas lainnya yang mengekstrak metadata dari gambar.
Aktivitas thread utama di aplikasi ekstraktor metadata gambar. Perhatikan bahwa semua aktivitas akan terjadi di thread utama.

Perlu diingat bahwa—selain thread lain yang mungkin ada, seperti rasterizer thread dan seterusnya—semua yang ada dalam aplikasi terjadi di thread utama. Di halaman utama , hal berikut akan terjadi:

  1. Formulir ini mengambil input dan mengirimkan permintaan fetch untuk mendapatkan input awal gambar yang berisi metadata Exif.
  2. Data gambar dikonversi menjadi ArrayBuffer.
  3. Skrip exif-reader digunakan untuk mengekstrak metadata Exif dari gambar.
  4. Metadata disalin untuk membentuk {i>string<i} HTML, yang kemudian mengisi penampil metadata.

Sekarang bandingkan dengan penerapan perilaku yang sama—tetapi menggunakan pekerja!

Tampilan thread utama dengan pekerja web

Sekarang Anda telah melihat seperti apa yang terlihat untuk mengekstrak {i>metadata<i} Exif dari file JPEG di thread utama, lihat bagaimana tampilannya pekerja adalah kombinasi dari:

  1. Buka tab lain di Chrome, dan buka DevTools.
  2. Buka panel performa.
  3. Buka https://exif-worker.glitch.me/with-worker.html.
  4. Di panel performa, klik tombol rekam di kanan atas pojok panel DevTools.
  5. Tempel link gambar ini di kolom, lalu klik tombol Get that JPEG!.
  6. Setelah antarmuka diisi dengan metadata Exif, klik tombol rekam lagi untuk berhenti merekam.
Performance profiler menampilkan aktivitas aplikasi ekstraktor metadata gambar yang terjadi di thread utama dan thread pekerja web. Meskipun masih ada tugas yang berjalan lama di thread utama, tugas tersebut jauh lebih pendek, dengan pengambilan/decoding gambar dan ekstraksi metadata yang sepenuhnya terjadi di thread pekerja web. Satu-satunya pekerjaan thread utama melibatkan penerusan data ke dan dari pekerja web.
Aktivitas thread utama di aplikasi ekstraktor metadata gambar. Perhatikan bahwa ada thread pekerja web tambahan tempat sebagian besar pekerjaan dilakukan.

Ini adalah kekuatan pekerja web. Daripada melakukan semuanya di bagian utama utas, semuanya kecuali mengisi penampil metadata dengan HTML dilakukan pada thread terpisah. Ini berarti thread utama dibebaskan untuk melakukan pekerjaan lain.

Mungkin keuntungan terbesarnya adalah, tidak seperti versi aplikasi ini yang tidak menggunakan pekerja web, skrip exif-reader tidak dimuat di halaman tetapi lebih ke thread pekerja web. Ini berarti bahwa biaya mendownload, mengurai, dan mengompilasi skrip exif-reader berlangsung dari thread utama.

Sekarang, mari pelajari kode pekerja web yang membuat semua ini dapat dilakukan!

Memahami kode pekerja web

Tidaklah cukup untuk melihat perbedaan yang dibuat oleh pekerja web, tetapi juga membantu memahami — setidaknya dalam hal ini — seperti apa kode itu terlihat sehingga Anda tahu apa yang mungkin dalam cakupan pekerja web.

Mulai dengan kode thread utama yang perlu terjadi sebelum pekerja web bisa masukkan gambar:

// 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';
});

Kode ini berjalan pada thread utama, dan menyiapkan formulir untuk mengirim URL gambar ke pekerja web. Dari sana, kode pekerja web dimulai dengan importScripts yang memuat skrip exif-reader eksternal, lalu menyiapkan pipeline pesan ke thread utama:

// 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);
  });
});

Bagian kecil JavaScript ini menyiapkan pipeline pesan sehingga saat pengguna mengirimkan formulir dengan URL ke file JPEG, URL tersebut masuk di pekerja web. Dari sana, bit kode berikutnya mengekstrak {i>metadata<i} Exif dari file JPEG, membuat string HTML, dan mengirimkan HTML tersebut kembali ke window agar pada akhirnya menjadi ditampilkan kepada pengguna:

// 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('')
      });
    });
  });
});

Agak mudah untuk dibaca, tetapi ini juga merupakan kasus penggunaan yang cukup rumit untuk pekerja web. Namun, hasilnya sepadan dengan pekerjaan yang dilakukan, dan tidak hanya terbatas pada kasus penggunaan ini. Anda dapat menggunakan pekerja web untuk berbagai hal, seperti mengisolasi panggilan fetch dan memproses respons, memproses data dalam jumlah besar tanpa memblokir di thread utama—dan itu hanya permulaan.

Saat meningkatkan kinerja aplikasi web Anda, mulailah berpikir tentang apa pun yang dapat dilakukan secara wajar dalam konteks pekerja web. Keuntungan dapat berupa signifikan, dan dapat menghasilkan pengalaman pengguna yang lebih baik secara keseluruhan untuk {i>website<i} Anda.