Kasus penggunaan pekerja web konkret

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

Salah satu kasus penggunaan tersebut dapat berupa situs yang perlu menghapus metadata Exif dari gambar. Ini bukanlah konsep yang terlalu kaku. Bahkan, situs seperti Flickr menawarkan cara kepada pengguna untuk melihat metadata Exif guna mempelajari detail teknis tentang gambar yang mereka host, seperti kedalaman warna, merek dan model kamera, serta data lainnya.

Namun, logika untuk mengambil gambar, mengonversinya menjadi ArrayBuffer, dan mengekstrak metadata Exif dapat berpotensi mahal jika dilakukan sepenuhnya di thread utama. Untungnya, cakupan pekerja web memungkinkan pekerjaan ini yang dilakukan dari thread utama. Kemudian, dengan menggunakan pipeline pesan pekerja web, metadata Exif akan ditransmisikan kembali ke thread utama sebagai string HTML, dan ditampilkan kepada pengguna.

Tampilan 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 Record di sudut kanan atas panel DevTools.
  5. Tempel link gambar ini—atau link lain pilihan Anda yang berisi metadata Exif—di kolom, lalu klik tombol Dapatkan JPEG!.
  6. Setelah antarmuka diisi dengan metadata Exif, klik Record lagi untuk berhenti merekam.
Profiler performa yang menampilkan aktivitas aplikasi ekstraktor metadata gambar yang terjadi sepenuhnya di thread utama. Ada dua tugas penting yang panjang—satu tugas menjalankan pengambilan untuk mendapatkan gambar yang diminta dan mendekodenya, dan satu lagi yang mengekstrak metadata dari gambar.
Aktivitas thread utama di aplikasi ekstraktor metadata gambar. Perlu diperhatikan bahwa semua aktivitas terjadi di thread utama.

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

  1. Formulir ini akan mengambil input dan mengirimkan permintaan fetch untuk mendapatkan bagian awal dari 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 membuat string HTML, yang kemudian mengisi penampil metadata.

Sekarang, bandingkan dengan penerapan perilaku yang sama—tetapi menggunakan pekerja web.

Tampilan thread utama dengan pekerja web

Setelah Anda melihat proses mengekstrak metadata Exif dari file JPEG di thread utama, lihat tampilan saat pekerja web digabungkan:

  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 record di sudut kanan atas panel DevTools.
  5. Tempel link gambar ini di kolom, lalu klik tombol Dapatkan JPEG!.
  6. Setelah antarmuka diisi dengan metadata Exif, klik tombol record lagi untuk berhenti merekam.
Profiler performa yang menampilkan aktivitas aplikasi ekstraktor metadata gambar yang terjadi di thread utama dan thread web worker. Meskipun masih ada tugas yang panjang di thread utama, tugas tersebut jauh lebih pendek, dengan pengambilan/dekode gambar dan ekstraksi metadata yang terjadi sepenuhnya 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. Perlu diketahui bahwa ada thread pekerja web tambahan tempat sebagian besar pekerjaan diselesaikan.

Ini adalah kekuatan pekerja web. Alih-alih melakukan semuanya di thread utama, semuanya kecuali mengisi penampil metadata dengan HTML dilakukan pada thread terpisah. Ini berarti thread utama dibebaskan untuk melakukan tugas lain.

Mungkin keuntungan terbesar di sini adalah, tidak seperti versi aplikasi ini yang tidak menggunakan pekerja web, skrip exif-reader tidak dimuat di thread utama, melainkan di thread pekerja web. Artinya, biaya untuk mendownload, mengurai, dan mengompilasi skrip exif-reader akan dibebankan di luar thread utama.

Sekarang untuk mendalami kode pekerja web yang memungkinkan semua ini.

Melihat kode pekerja web

Melihat perbedaan yang dibuat pekerja web tidaklah cukup, tetapi juga membantu memahami—setidaknya dalam kasus ini—seperti apa kode tersebut sehingga Anda tahu apa yang mungkin dilakukan dalam cakupan pekerja web.

Mulailah dengan kode thread utama yang perlu terjadi sebelum pekerja web dapat memasukkan 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 pernyataan 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);
  });
});

Bit JavaScript ini menyiapkan pipeline pesan sehingga saat pengguna mengirimkan formulir dengan URL ke file JPEG, URL tersebut tiba di pekerja web. Dari sana, bit kode berikutnya ini mengekstrak metadata Exif dari file JPEG, membuat string HTML, dan mengirimkan HTML tersebut kembali ke window untuk akhirnya 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 sulit dibaca, tetapi ini juga merupakan kasus penggunaan yang cukup rumit bagi pekerja web. Namun, hasilnya sepadan dengan hasil kerja, dan tidak hanya terbatas pada kasus penggunaan ini. Anda dapat menggunakan pekerja web untuk segala macam hal, seperti mengisolasi panggilan fetch dan memproses respons, memproses data dalam jumlah besar tanpa memblokir thread utama—dan itu hanya sebagai pemicu.

Saat meningkatkan performa aplikasi web Anda, mulailah memikirkan apa pun yang dapat dilakukan secara wajar dalam konteks pekerja web. Peningkatannya bisa signifikan, dan dapat menghasilkan pengalaman pengguna yang lebih baik secara keseluruhan untuk situs Anda.