תרחיש לדוגמה לשימוש ב-Web Worker

במודול האחרון הוצגה סקירה כללית של עובדי אינטרנט. עובדי אינטרנט יכולים לשפר את תגובתיות הקלט על ידי העברת JavaScript מהשרשור הראשי שרשורים נפרדים של עובדי אינטרנט, שיכולים לעזור לשפר את האינטראקציה של האתר שלך עד Next Paint (INP) כשעובדים שלא צריכים גישה ישירה אל ה-thread הראשי. עם זאת, סקירה כללית בלבד אינה מספיקה, ובמודול הזה יש מוצע תרחיש קונקרטי לדוגמה של עובד אינטרנט.

אחד התרחישים לדוגמה האלה יכול להיות אתר שצריך להסיר ממנו מטא-נתונים של תצוגת Exif. תמונה — זה לא רעיון רחוק. למעשה, אתרים כמו Flickr מציעים להציג את המטא-נתונים של תצוגת Exif כדי ללמוד פרטים טכניים על בתמונות שהם מארחים, כמו עומק הצבעים, היצרן והדגם של המצלמה ועוד .

אבל הלוגיקה של אחזור תמונה והמרה שלה ל-ArrayBuffer, וחילוץ המטא-נתונים של תצוגת Exif עלול להיות יקר אם ייעשה בו שימוש מלא בשרשור הראשי. למרבה המזל, היקף ה-Web Worker מאפשר לבצע את העבודה הזו מחוץ לשרשור הראשי. לאחר מכן, באמצעות צינור עיבוד הנתונים של עובד האינטרנט, המטא-נתונים של תצוגת Exif מוחזרים ל-thread הראשי כמחרוזת HTML, שמוצגת למשתמש.

איך נראה ה-thread הראשי בלי לעבוד עם Weber

קודם כול, בודקים איך נראה ה-thread הראשי כשאנחנו מבצעים את העבודה הזו בלי Web Worker. כדי לעשות זאת:

  1. פותחים כרטיסייה חדשה ב-Chrome ופותחים את כלי הפיתוח שלה.
  2. פותחים את חלונית הביצועים.
  3. עוברים לכתובת https://exif-worker.glitch.me/without-worker.html.
  4. בחלונית הביצועים, לוחצים על הקלטה בפינה השמאלית העליונה של בחלונית של כלי הפיתוח.
  5. מדביקים את הקישור לתמונה הזו או קישור אחר לבחירתכם שמכיל את תצוגת Exif. מטא-נתונים—בשדה, ולחץ על הלחצן קבלו את JPEG!.
  6. אחרי שהממשק מאוכלס במטא-נתונים של תצוגת Exif, לוחצים שוב על Record (הקלטה) כדי. הפסקת ההקלטה.
כלי לניתוח הביצועים שמציג את הפעילות של האפליקציה לחילוץ מטא-נתונים של התמונה שמתרחשת אך ורק ב-thread הראשי. יש שתי משימות ארוכות משמעותית - אחת שמפעילה אחזור כדי לקבל את התמונה המבוקשת ולפענח אותה, והשנייה שמחלצת את המטא-נתונים מהתמונה.
פעילות שרשור ראשית באפליקציה לחילוץ מטא-נתונים של תמונות. חשוב לשים לב שכל החישובים של הפעילות מתרחשת ב-thread הראשי.

שימו לב ש-מלבד שרשורים אחרים שעשויים להיות, כגון כלי ליצירת רסטר שרשורים וכן הלאה – כל מה באפליקציה מופיע ב-thread הראשי. בשידור הראשי יתרחשו הדברים הבאים:

  1. הטופס מקבל את הקלט ושולח בקשת fetch כדי לקבל את הבקשה הראשונית של התמונה שמכילה את המטא-נתונים של תצוגת Exif.
  2. נתוני התמונה מומרים ל-ArrayBuffer.
  3. הסקריפט exif-reader משמש לחילוץ המטא-נתונים של תצוגת Exif תמונה.
  4. המטא-נתונים מועתקים כדי ליצור מחרוזת HTML, שמאכלסת את הנתונים מציג המטא-נתונים.

כעת נשווה זאת עם יישום של אותה התנהגות - אבל שימוש עובד!

איך נראה ה-thread הראשי עם עובד אינטרנט

עכשיו, אחרי שראיתם איך נראים האופן שבו אפשר לחלץ את המטא-נתונים של תצוגת Exif קובץ JPEG ב-thread הראשי. כדאי לבדוק איך הוא נראה מהאינטרנט משולב:

  1. פותחים כרטיסייה אחרת ב-Chrome ופותחים את כלי הפיתוח שלה.
  2. פותחים את חלונית הביצועים.
  3. עוברים אל https://exif-worker.glitch.me/with-worker.html.
  4. בחלונית הביצועים, לוחצים על לחצן ההקלטה בפינה השמאלית העליונה. החלונית של כלי הפיתוח.
  5. מדביקים את הקישור לתמונה הזו בשדה ולוחצים על הלחצן Get this JPEG!.
  6. אחרי שהממשק מאוכלס במטא-נתונים של תצוגת Exif, לוחצים על לחצן ההקלטה. שוב כדי להפסיק את ההקלטה.
כלי לניתוח הביצועים שמציג את הפעילות של האפליקציה לחילוץ מטא-נתונים של התמונה שמתרחשת גם ב-thread הראשי וגם ב-thread של עבודה באינטרנט. ב-thread הראשי עדיין יש משימות ארוכות, אבל הן קצרות מאוד, ואחזור/פענוח של תמונות וחילוץ מטא-נתונים מתרחשים אך ורק ב-thread של worker באינטרנט. תהליך העבודה היחיד של ה-thread הראשי הוא העברת נתונים אל עובד האינטרנט וממנו.
פעילות שרשור ראשית באפליקציה לחילוץ מטא-נתונים של תמונות. שימו לב שיש שרשור נוסף של Web Worker שבו מתבצעת רוב העבודה.

זוהי הכוח של עובד אינטרנט. במקום לעשות הכול שרשור, הכל חוץ מאכלוס מציג המטא-נתונים ב-HTML מתבצעת שרשור נפרד. כלומר, ה-thread הראשי פנוי כדי לבצע עבודות אחרות.

יכול להיות שהיתרון הכי גדול כאן הוא, בניגוד לגרסת האפליקציה הזאת לא משתמש ב-Web worker, הסקריפט exif-reader לא נטען אלא ב-thread של עבודה באינטרנט. המשמעות היא שהעלות של הורדה, ניתוח והידור של הסקריפט exif-reader מתרחש מחוץ ה-thread הראשי.

עכשיו נצלול לקוד של Web Worker שהופך את כל זה לאפשרי!

הצצה בקוד של Web Worker

לא מספיק לראות את ההבדל שעובד אינטרנט עושה, אלא גם עוזר להבין – לפחות במקרה הזה – איך הקוד הזה נראה, כדי שתדעו מהו בתחום של Web Worker.

התחלה בקוד של ה-thread הראשי שצריך להתרחש לפני שעובד האינטרנט יכול הזן את התמונה:

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

הקוד הזה פועל ב-thread הראשי ומגדיר את הטופס כדי לשלוח את כתובת ה-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 ועיבוד תגובות, עיבוד כמויות גדולות של נתונים מבלי לחסום את של השרשור הראשי, וזהו רק בתור התחלה.

כשאתם משפרים את הביצועים של אפליקציות האינטרנט, כדאי להתחיל לחשוב כל דבר שניתן לבצע באופן סביר בהקשר של עובדי אינטרנט. הרווחים יכולים להיות משמעותי ויכול להוביל לחוויית משתמש טובה יותר באתר שלך.