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

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

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

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

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

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

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

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

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

ตอนนี้แตกต่างจากการใช้ลักษณะการทำงานเดิม แต่เป็นการใช้ Web Worker

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

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

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

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

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

มาเจาะลึกโค้ด Web Work ที่ทำให้ทั้งหมดนี้เกิดขึ้นได้กัน

ดูรหัส 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://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('')
      });
    });
  });
});

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

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