กรณีการใช้งานของ Web Worker ที่เป็นรูปธรรม

ในโมดูลที่แล้ว เราได้กล่าวถึงภาพรวมของ Web Worker Web Worker ช่วยปรับปรุงการตอบสนองต่ออินพุตได้โดยการย้าย JavaScript ออกจากเทรดหลักไปยังเทรด Web Worker แยกต่างหาก ซึ่งจะช่วยปรับปรุงการโต้ตอบ จนถึงการแสดงผลถัดไป (INP) ของเว็บไซต์เมื่อคุณมีงานที่ไม่จำเป็นต้องเข้าถึงเทรดหลักโดยตรง อย่างไรก็ตาม ภาพรวมเพียงอย่างเดียวไม่เพียงพอ และในโมดูลนี้ เราจะนำเสนอ กรณีการใช้งานที่ชัดเจนสำหรับ Web Worker

กรณีการใช้งานดังกล่าวอาจเป็นเว็บไซต์ที่ต้องลบข้อมูลเมตา Exif ออกจากรูปภาพ ซึ่งไม่ใช่แนวคิดที่ไกลเกินจริง ในความเป็นจริงแล้ว เว็บไซต์อย่าง Flickr มีวิธีให้ผู้ใช้ดูข้อมูลเมตา Exif เพื่อดูรายละเอียดทางเทคนิคเกี่ยวกับรูปภาพที่โฮสต์ เช่น ความลึกของสี ยี่ห้อและรุ่นของกล้อง และข้อมูลอื่นๆ

อย่างไรก็ตาม ตรรกะในการดึงรูปภาพ แปลงเป็น ArrayBuffer และแยกข้อมูลเมตา Exif อาจมีค่าใช้จ่ายสูงหากดำเนินการทั้งหมด ในเทรดหลัก โชคดีที่ขอบเขตของ Web Worker อนุญาตให้ดำเนินการนี้ นอกชุดข้อความหลักได้ จากนั้นระบบจะส่งข้อมูลเมตา Exif กลับไปยังเธรดหลักเป็นสตริง HTML และแสดงต่อผู้ใช้โดยใช้ไปป์ไลน์การรับส่งข้อความของ Web Worker

ลักษณะของเทรดหลักเมื่อไม่มี Web Worker

ก่อนอื่น ให้สังเกตลักษณะของเทรดหลักเมื่อเราทำงานนี้โดยไม่มี Web Worker โดยทำตามขั้นตอนต่อไปนี้

  1. เปิดแท็บใหม่ใน Chrome แล้วเปิดเครื่องมือสำหรับนักพัฒนาเว็บ
  2. เปิดแผงประสิทธิภาพ
  3. ไปที่ https://chrome.dev/learn-performance-exif-worker/without-worker.html
  4. ในแผงประสิทธิภาพ ให้คลิกบันทึกที่มุมขวาบนของ บานหน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บ
  5. วางลิงก์รูปภาพนี้หรือลิงก์อื่นที่คุณเลือกซึ่งมีข้อมูลเมตา Exif ในช่อง แล้วคลิกปุ่มรับ JPEG นั้น
  6. เมื่ออินเทอร์เฟซแสดงข้อมูลเมตา Exif แล้ว ให้คลิกบันทึกอีกครั้งเพื่อ หยุดบันทึก
เครื่องมือสร้างโปรไฟล์ประสิทธิภาพที่แสดงกิจกรรมของแอปตัวแยกข้อมูลเมตาของรูปภาพซึ่งเกิดขึ้นในเทรดหลักทั้งหมด โดยมีงานที่ใช้เวลานาน 2 งานที่สำคัญ ได้แก่ งานที่เรียกข้อมูลเพื่อรับรูปภาพที่ขอและถอดรหัสรูปภาพ และอีกงานที่ดึงข้อมูลเมตาจากรูปภาพ
กิจกรรมในเทรดหลักในแอปเครื่องมือแยกข้อมูลเมตาของรูปภาพ โปรดทราบว่ากิจกรรมทั้งหมด เกิดขึ้นในเทรดหลัก

โปรดทราบว่าทุกอย่างในแอปจะเกิดขึ้นในเทรดหลัก นอกเหนือจากเทรดอื่นๆ ที่อาจมีอยู่ เช่น เทรด Rasterizer เป็นต้น ในเทรดหลักจะเกิดสิ่งต่อไปนี้

  1. แบบฟอร์มจะรับอินพุตและส่งคำขอ fetch เพื่อรับส่วนเริ่มต้นของรูปภาพที่มีข้อมูลเมตา Exif
  2. ระบบจะแปลงข้อมูลรูปภาพเป็น ArrayBuffer
  3. สคริปต์ exif-reader ใช้เพื่อดึงข้อมูลเมตา Exif จากรูปภาพ
  4. ระบบจะคัดลอกข้อมูลเมตาเพื่อสร้างสตริง HTML จากนั้นจะป้อนข้อมูลลงใน โปรแกรมดูข้อมูลเมตา

ตอนนี้ลองเปรียบเทียบกับการใช้งานลักษณะการทำงานเดียวกัน แต่ใช้ Web Worker

ลักษณะของเทรดหลักเมื่อมี Web Worker

ตอนนี้คุณได้เห็นลักษณะการดึงข้อมูลเมตา Exif จากไฟล์ JPEG ในเทรดหลักแล้ว มาดูลักษณะการดึงข้อมูลเมื่อมี Web Worker เข้ามาเกี่ยวข้องกัน

  1. เปิดแท็บอื่นใน Chrome แล้วเปิดเครื่องมือสำหรับนักพัฒนาเว็บ
  2. เปิดแผงประสิทธิภาพ
  3. ไปที่ https://chrome.dev/learn-performance-exif-worker/with-worker.html
  4. ในแผงประสิทธิภาพ ให้คลิกปุ่มบันทึกที่มุมขวาบน ของแผงเครื่องมือสำหรับนักพัฒนาเว็บ
  5. วางลิงก์รูปภาพนี้ในช่อง แล้วคลิกปุ่มรับ JPEG นั้น
  6. เมื่ออินเทอร์เฟซแสดงข้อมูลเมตา Exif แล้ว ให้คลิกปุ่มบันทึก อีกครั้งเพื่อหยุดบันทึก
โปรไฟล์ประสิทธิภาพที่แสดงกิจกรรมของแอปตัวแยกข้อมูลเมตาของรูปภาพซึ่งเกิดขึ้นทั้งในเทรดหลักและเทรด Web Worker แม้ว่าชุดข้อความหลักจะยังมีงานที่ใช้เวลานาน แต่ก็สั้นลงอย่างมาก โดยการดึง/ถอดรหัสรูปภาพและการแยกข้อมูลเมตาจะเกิดขึ้นในชุดข้อความของ Web Worker ทั้งหมด งานในชุดข้อความหลักมีเพียงการส่งข้อมูลไปยังและจาก Web Worker
กิจกรรมของเธรดหลักในแอปเครื่องมือแยกข้อมูลเมตาของรูปภาพ โปรดทราบว่ามีเธรด Web Worker เพิ่มเติมซึ่งใช้ในการทำงานส่วนใหญ่

นี่คือพลังของ Web Worker แทนที่จะทำทุกอย่างในเทรดหลัก ทุกอย่างยกเว้นการแสดงผลโปรแกรมดูข้อมูลเมตาด้วย HTML จะทำในเทรดแยกต่างหาก ซึ่งหมายความว่าเทรดหลักจะว่างเพื่อทำงานอื่นๆ

ข้อได้เปรียบที่สำคัญที่สุดในที่นี้คือสคริปต์ exif-reader จะไม่โหลดในเทรดหลัก แต่จะโหลดในเทรดของ Web Worker ซึ่งแตกต่างจากแอปเวอร์ชันที่ไม่ได้ใช้ Web Worker ซึ่งหมายความว่าค่าใช้จ่ายในการดาวน์โหลด แยกวิเคราะห์ และคอมไพล์สคริปต์ exif-reader จะเกิดขึ้นในเธรดอื่นที่ไม่ใช่เธรดหลัก

มาดูโค้ด Web Worker ที่ทำให้ทุกอย่างนี้เป็นไปได้กัน

ดูโค้ด Web Worker

การดูความแตกต่างที่ Web Worker สร้างขึ้นยังไม่เพียงพอ แต่ยังช่วยให้คุณเข้าใจได้ด้วยว่าโค้ดนั้นมีลักษณะอย่างไร (อย่างน้อยก็ในกรณีนี้) เพื่อให้คุณทราบว่ามีอะไรบ้างที่ทำได้ในขอบเขตของ Web Worker

เริ่มต้นด้วยโค้ดในเทรดหลักที่ต้องเกิดขึ้นก่อนที่ Web Worker จะ เข้ามามีบทบาท

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

โค้ดนี้จะทำงานในเทรดหลัก และตั้งค่าแบบฟอร์มเพื่อส่ง URL ของรูปภาพไปยัง Web Worker จากนั้นโค้ด Web Worker จะเริ่มต้นด้วยคำสั่ง importScripts ที่โหลดสคริปต์ exif-reader ภายนอก แล้วตั้งค่า ไปป์ไลน์การรับส่งข้อความไปยังเทรดหลัก

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

JavaScript บรรทัดนี้จะตั้งค่าไปป์ไลน์การรับส่งข้อความเพื่อให้เมื่อผู้ใช้ส่งแบบฟอร์มที่มี URL ไปยังไฟล์ JPEG แล้ว URL จะไปถึง Web Worker จากนั้น โค้ดส่วนถัดไปนี้จะดึงข้อมูลเมตา Exif จากไฟล์ JPEG สร้างสตริง HTML และส่ง HTML นั้นกลับไปยัง window เพื่อให้แสดงต่อผู้ใช้ในที่สุด

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

แม้ว่าจะมีเนื้อหาให้อ่านอยู่บ้าง แต่กรณีการใช้งานนี้ก็ค่อนข้างซับซ้อนสำหรับ Web Worker อย่างไรก็ตาม ผลลัพธ์ที่ได้นั้นคุ้มค่ากับความพยายาม และไม่ได้จำกัดอยู่แค่กรณีการใช้งานนี้ คุณสามารถใช้ Web Worker สำหรับสิ่งต่างๆ ได้มากมาย เช่น การแยกการเรียก fetch และการประมวลผลการตอบกลับ การประมวลผลข้อมูลจำนวนมากโดยไม่บล็อก เธรดหลัก และอื่นๆ อีกมากมาย

เมื่อปรับปรุงประสิทธิภาพของเว็บแอปพลิเคชัน ให้เริ่มคิดถึง สิ่งต่างๆ ที่ทำได้อย่างสมเหตุสมผลในบริบทของ Web Worker ซึ่งอาจส่งผลให้เว็บไซต์ของคุณมีประสบการณ์การใช้งานโดยรวมที่ดีขึ้นอย่างมาก