Trường hợp sử dụng cụ thể của web worker

Trong học phần cuối cùng, chúng tôi đã cung cấp tổng quan về trình thực thi web. Nhân viên web có thể cải thiện khả năng phản hồi đầu vào bằng cách chuyển JavaScript ra khỏi luồng chính sang các luồng trình chạy web riêng biệt, điều này có thể giúp cải thiện Tương tác của trang web của bạn sang Next Paint (INP) khi bạn có công việc không cần truy cập trực tiếp vào luồng chính. Tuy nhiên, nếu chỉ xem thông tin tổng quan là chưa đủ, trong học phần này, có một trường hợp sử dụng cụ thể cho nhân viên web.

Một trường hợp sử dụng như vậy có thể là việc trang web cần tách siêu dữ liệu Exif khỏi hình ảnh—đây không phải là một khái niệm xa vời. Trên thực tế, các trang web như Flickr cung cấp người dùng có thể xem siêu dữ liệu Exif để tìm hiểu chi tiết kỹ thuật về hình ảnh mà máy ảnh lưu trữ, chẳng hạn như độ sâu màu, nhãn hiệu máy ảnh, mẫu máy ảnh và các thông tin khác .

Tuy nhiên, logic để tìm nạp hình ảnh, chuyển đổi hình ảnh đó thành ArrayBuffer, và việc trích xuất siêu dữ liệu Exif có thể tốn kém nếu thực hiện hoàn toàn trên luồng chính. Rất may là phạm vi trình chạy web cho phép hoàn tất công việc này ra khỏi luồng chính. Sau đó, bằng cách sử dụng quy trình nhắn tin của nhân viên web, Siêu dữ liệu Exif được truyền trở lại luồng chính dưới dạng chuỗi HTML và hiển thị cho người dùng.

Luồng chính sẽ trông như thế nào khi không có nhân viên web

Trước tiên, hãy quan sát xem luồng chính trông như thế nào khi chúng ta thực hiện công việc này mà không cần nhân viên web. Để cập nhật, hãy thực hiện các bước sau:

  1. Mở một thẻ mới trong Chrome rồi mở Công cụ cho nhà phát triển.
  2. Mở bảng hiệu suất.
  3. Truy cập vào https://exif-worker.glitch.me/without-worker.html.
  4. Trong bảng điều khiển hiệu suất, hãy nhấp vào Ghi ở góc trên bên phải của ngăn Công cụ cho nhà phát triển.
  5. Dán đường liên kết của hình ảnh này hoặc một liên kết khác mà bạn chọn có chứa Exif siêu dữ liệu — trong trường và nhấp vào nút Lấy tệp JPEG đó!.
  6. Sau khi giao diện điền siêu dữ liệu Exif, hãy nhấp lại vào Record (Ghi) để dừng ghi âm.
Trình phân tích hiệu suất cho thấy hoạt động của ứng dụng trích xuất siêu dữ liệu hình ảnh diễn ra hoàn toàn trên luồng chính. Có hai tác vụ dài đáng kể — một tác vụ chạy tìm nạp để lấy hình ảnh được yêu cầu và giải mã, tác vụ còn lại trích xuất siêu dữ liệu từ hình ảnh.
Hoạt động của luồng chính trong ứng dụng trích xuất siêu dữ liệu hình ảnh. Lưu ý rằng tất cả hoạt động xảy ra trên luồng chính.

Lưu ý rằng – ngoài các luồng khác có thể xuất hiện, chẳng hạn như trình tạo điểm ảnh luồng, v.v. – mọi thứ trong ứng dụng đều diễn ra trên luồng chính. Trên nền chính luồng thì những điều sau đây sẽ xảy ra:

  1. Biểu mẫu này sẽ nhận thông tin đầu vào và gửi yêu cầu fetch để lấy thông tin ban đầu của hình ảnh chứa siêu dữ liệu Exif.
  2. Dữ liệu hình ảnh sẽ được chuyển đổi thành ArrayBuffer.
  3. Tập lệnh exif-reader dùng để trích xuất siêu dữ liệu Exif từ hình ảnh.
  4. Siêu dữ liệu được trích xuất để tạo chuỗi HTML, sau đó điền trình xem siêu dữ liệu.

Giờ thì hãy đối chiếu với việc triển khai cùng một hành vi, nhưng sử dụng web !

Luồng chính sẽ trông như thế nào với một trình chạy web

Giờ thì bạn đã biết mọi thứ trông như thế nào khi trích xuất siêu dữ liệu Exif từ Tệp JPEG trên luồng chính, hãy xem tệp này trông như thế nào khi web worker là kết hợp:

  1. Mở một thẻ khác trong Chrome rồi mở Công cụ cho nhà phát triển của trình duyệt đó.
  2. Mở bảng hiệu suất.
  3. Truy cập vào https://exif-worker.glitch.me/with-worker.html.
  4. Trong bảng hiệu suất, hãy nhấp vào nút ghi lại ở phía trên bên phải góc của ngăn Công cụ cho nhà phát triển.
  5. Dán đường liên kết của hình ảnh này vào trường và nhấp vào nút Tải tệp JPEG đó!.
  6. Sau khi giao diện điền siêu dữ liệu Exif, hãy nhấp vào nút ghi để dừng ghi.
Trình phân tích hiệu suất cho thấy hoạt động của ứng dụng trích xuất siêu dữ liệu hình ảnh diễn ra trên cả luồng chính và luồng worker web. Mặc dù vẫn còn những tác vụ dài trên luồng chính, nhưng các tác vụ này sẽ ngắn hơn đáng kể, với việc tìm nạp/giải mã hình ảnh và trích xuất siêu dữ liệu diễn ra hoàn toàn trên luồng worker web. Công việc theo luồng chính duy nhất liên quan đến việc truyền dữ liệu đến và từ trình chạy web.
Hoạt động của luồng chính trong ứng dụng trích xuất siêu dữ liệu hình ảnh. Lưu ý rằng có một luồng khác của nhân viên web, nơi thực hiện hầu hết công việc.

Đây là sức mạnh của nhân viên web. Thay vì làm mọi việc trên luồng, mọi thứ trừ việc điền sẵn trình xem siêu dữ liệu bằng HTML được thực hiện trên chuỗi riêng biệt. Tức là luồng chính được giải phóng để thực hiện công việc khác.

Có lẽ lợi thế lớn nhất ở đây là, không giống như phiên bản ứng dụng này không sử dụng trình chạy web, nên tập lệnh exif-reader không được tải trên mà là trên luồng worker web. Điều này có nghĩa là chi phí của việc tải xuống, phân tích cú pháp và biên dịch tập lệnh exif-reader sẽ diễn ra ngoài luồng chính.

Bây giờ, hãy cùng tìm hiểu sâu hơn về mã của nhân viên web giúp thực hiện tất cả những điều này!

Tìm hiểu mã của trình chạy web

Việc thấy được sự khác biệt mà một nhân viên web tạo ra là chưa đủ, mà còn hiểu – ít nhất là trong trường hợp này – mã đó trông như thế nào để bạn biết có thể thực hiện trong phạm vi trình chạy web.

Bắt đầu bằng mã luồng chính cần diễn ra trước khi worker web có thể nhập hình ảnh:

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

Mã này chạy trên luồng chính và thiết lập biểu mẫu để gửi URL hình ảnh đến nhân viên web. Tại đó, mã trình chạy web bắt đầu bằng importScripts tải tập lệnh exif-reader bên ngoài, sau đó thiết lập quy trình nhắn tin đến luồng chính:

// 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 này thiết lập đường dẫn thông báo để khi người dùng gửi biểu mẫu có URL đến tệp JPEG, URL đến trong trình chạy web. Từ đó, đoạn mã tiếp theo này trích xuất siêu dữ liệu Exif từ tệp JPEG, tạo một chuỗi HTML rồi gửi HTML đó trở lại window để cuối cùng được hiển thị cho người dùng:

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

Hơi hơi khó đọc, nhưng đây cũng là một trường hợp sử dụng tương đối cần thiết cho nhân viên web. Tuy nhiên, kết quả xứng đáng với công sức bỏ ra chứ không chỉ giới hạn ở trường hợp sử dụng này. Bạn có thể sử dụng nhân viên web cho mọi việc, chẳng hạn như cách ly lệnh gọi fetch cũng như xử lý phản hồi, xử lý một lượng lớn dữ liệu mà không chặn luồng chính – và đó chỉ dành cho người mới bắt đầu.

Khi cải thiện hiệu suất của các ứng dụng web, hãy bắt đầu suy nghĩ về bất kỳ việc gì có thể thực hiện hợp lý trong ngữ cảnh web worker. Lợi ích có thể là đáng kể và có thể dẫn đến trải nghiệm người dùng tổng thể tốt hơn cho trang web của bạn.