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

Trong mô-đun trước, chúng tôi đã trình bày về tổng quan về trình thực thi web. Trình thực thi web có thể cải thiện khả năng phản hồi đầu vào bằng cách di chuyển JavaScript ra khỏi luồng chính sang các luồng trình thực thi web riêng biệt. Điều này có thể giúp cải thiện Hoạt động tương tác với thời gian tương tác với thời gian hiển thị tiếp theo (INP) trên trang web của bạn khi bạn có tác vụ mà không cần quyền truy cập trực tiếp vào luồng chính. Tuy nhiên, chỉ riêng thông tin tổng quan là chưa đủ và trong mô-đun này, chúng tôi cũng đề xuất trường hợp sử dụng cụ thể cho trình thực thi web.

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

Tuy nhiên, logic để tìm nạp hình ảnh, chuyển đổi hình ảnh đó thành ArrayBuffer và trích xuất siêu dữ liệu Exif có thể gây tốn kém nếu được thực hiện hoàn toàn trên luồng chính. Rất may, phạm vi của trình chạy web cho phép thực hiện công việc này ngoài luồng chính. Sau đó, bằng quy trình thông báo của trình chạy 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.

Giao diện của luồng chính khi không có trình thực thi web

Trước tiên, hãy quan sát giao diện của luồng chính khi chúng ta thực hiện công việc này mà không cần trình thực thi 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 của Chrome.
  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 Record (Ghi lại) ở 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 đường liên kết khác trong số những hình ảnh bạn chọn chứa siêu dữ liệu Exif) vào trường này và nhấp vào nút Lấy ảnh JPEG đó!.
  6. Sau khi giao diện điền siêu dữ liệu Exif, hãy nhấp vào Record (Ghi) lần nữa để 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 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ã hình ảnh và một tác vụ khác trích xuất siêu dữ liệu từ hình ảnh.
Hoạt động luồng chính trong ứng dụng trích xuất siêu dữ liệu hình ảnh. Xin lưu ý rằng tất cả hoạt động đều diễn ra trên luồng chính.

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

  1. Biểu mẫu nhận dữ liệu đầu vào và gửi yêu cầu fetch để lấy phần đầu tiên 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 được dùng để trích xuất siêu dữ liệu Exif từ hình ảnh.
  4. Siêu dữ liệu được thu thập để tạo một chuỗi HTML, sau đó sẽ điền sẵn trình xem siêu dữ liệu.

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

Giao diện của luồng chính với một nhân viên web

Giờ bạn đã biết các bước trích xuất siêu dữ liệu Exif từ tệp JPEG trên luồng chính sẽ diễn ra như thế nào, hãy xem giao diện của một trình thực thi trên web đang 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 Chrome.
  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 điều khiển hiệu suất, hãy nhấp vào nút 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 vào trường và nhấp vào nút Get that JPEG! (Lấy ảnh JPEG đó!).
  6. Sau khi giao diện điền siêu dữ liệu Exif, hãy nhấp lại 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à một luồng worker web. Mặc dù vẫn có các tác vụ dài trên luồng chính, nhưng các tác vụ này ngắn hơn đáng kể, với quá trình 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. Tác vụ duy nhất của luồng chính liên quan đến việc truyền dữ liệu đến và đi từ trình thực thi web.
Hoạt động luồng chính trong ứng dụng trích xuất siêu dữ liệu hình ảnh. Xin lưu ý rằng có thêm một luồng worker web bổ sung để thực hiện hầu hết thao tác.

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

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

Bây giờ, hãy cùng tìm hiểu sâu về mã worker web giúp bạn làm được điều này!

Tìm hiểu về mã trình chạy web

Việc thấy được sự khác biệt mà một trình thực thi web tạo ra là chưa đủ. Nhưng ít nhất là trong trường hợp này, bạn cũng cần hiểu rõ mã đó trông như thế nào để bạn biết được những việc có thể làm trong phạm vi của 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ể vào 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 chuỗi chính và thiết lập biểu mẫu để gửi URL hình ảnh đến trình thực thi web. Từ đó, mã trình chạy web bắt đầu bằng câu lệnh importScripts sẽ tải tập lệnh exif-reader bên ngoài, sau đó thiết lập quy trình nhắn tin đến chuỗi 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 quy trình thông báo để khi người dùng gửi biểu mẫu có URL tới một tệp JPEG, URL đó sẽ đến trình thực thi web. Từ đó, đoạn mã tiếp theo này sẽ trích xuất siêu dữ liệu Exif từ tệp JPEG, tạo một chuỗi HTML và gửi HTML đó trở lại window để cuối cùng 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('')
      });
    });
  });
});

Bài viết này sẽ cần phải đọc một chút, nhưng đây cũng là một trường hợp sử dụng khá cần thiết cho nhân viên web. Tuy nhiên, kết quả mang lại xứng đáng với công sức bỏ ra và không chỉ giới hạn ở trường hợp sử dụng này. Bạn có thể sử dụng trình thực thi web cho mọi việc, chẳng hạn như tách biệt lệnh gọi fetch và xử lý phản hồi, xử lý lượng lớn dữ liệu mà không chặn luồng chính. Đây 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 nghĩ đến bất cứ điều gì có thể thực hiện hợp lý trong ngữ cảnh trình chạy web. Những lợi ích này có thể đáng kể và có thể mang lại trải nghiệm người dùng tổng thể tốt hơn cho trang web của bạn.