في الوحدة الأخيرة، تم تقديم نظرة عامة على Web Workers. يمكن أن تساعد Web Workers في تحسين سرعة الاستجابة للإدخال من خلال نقل JavaScript من سلسلة التعليمات الرئيسية إلى سلاسل تعليمات منفصلة خاصة بـ Web Workers، ما يساعد في تحسين التفاعل مع أول عرض (INP) على موقعك الإلكتروني عندما يكون لديك عمل لا يحتاج إلى الوصول المباشر إلى سلسلة التعليمات الرئيسية. ومع ذلك، لا تكفي النظرة العامة وحدها، وفي هذه الوحدة، يتم تقديم حالة استخدام ملموسة لأحد عاملي الويب.
أحد حالات الاستخدام هذه يمكن أن يكون موقعًا إلكترونيًا يحتاج إلى إزالة بيانات Exif الوصفية من صورة، وهذا ليس مفهومًا بعيد المنال. في الواقع، تتيح مواقع إلكترونية مثل Flickr للمستخدمين عرض بيانات Exif الوصفية للتعرّف على التفاصيل الفنية الخاصة بالصور التي تستضيفها، مثل عمق الألوان ونوع الكاميرا وطرازها وغيرها من البيانات.
ومع ذلك، قد تكون عملية جلب صورة وتحويلها إلى ArrayBuffer
واستخراج بيانات Exif الوصفية مكلفة إذا تم تنفيذها بالكامل على سلسلة التعليمات الرئيسية. لحسن الحظ، يتيح نطاق Web Worker تنفيذ هذا العمل خارج السلسلة الرئيسية. بعد ذلك، باستخدام مسار نقل الرسائل الخاص ببرنامج Web Worker، يتم نقل البيانات الوصفية Exif مرة أخرى إلى سلسلة التعليمات الرئيسية كسلسلة HTML، ويتم عرضها للمستخدم.
شكل السلسلة الرئيسية بدون عامل ويب
أولاً، لاحظ شكل سلسلة التعليمات الرئيسية عندما ننفّذ هذا العمل بدون عامل ويب. لإجراء ذلك، اتّبِع الخطوات التالية:
- افتح علامة تبويب جديدة في Chrome، ثم افتح "أدوات مطوّري البرامج" فيها.
- افتح لوحة الأداء.
- انتقِل إلى https://chrome.dev/learn-performance-exif-worker/without-worker.html.
- في لوحة "الأداء"، انقر على تسجيل في أعلى يسار جزء "أدوات مطوّري البرامج".
- الصِق رابط هذه الصورة أو رابطًا آخر من اختيارك يحتوي على بيانات وصفية بتنسيق Exif في الحقل، ثم انقر على الزر الحصول على ملف JPEG.
- بعد أن تتم تعبئة الواجهة بالبيانات الوصفية Exif، انقر على تسجيل مرة أخرى لإيقاف التسجيل.

يُرجى العِلم أنّه بالإضافة إلى سلاسل التعليمات الأخرى التي قد تكون موجودة، مثل سلاسل تعليمات تحويل الصور النقطية وما إلى ذلك، يحدث كل شيء في التطبيق على سلسلة التعليمات الرئيسية. في سلسلة التعليمات الرئيسية، يحدث ما يلي:
- يأخذ النموذج الإدخال ويرسل طلب
fetch
للحصول على الجزء الأولي من الصورة الذي يحتوي على بيانات Exif الوصفية. - يتم تحويل بيانات الصورة إلى
ArrayBuffer
. - يتم استخدام النص البرمجي
exif-reader
لاستخراج البيانات الوصفية Exif من الصورة. - يتم استخراج بيانات التعريف لإنشاء سلسلة HTML، ثم يتم ملء عارض بيانات التعريف بها.
والآن، لنقارن ذلك بتنفيذ السلوك نفسه باستخدام عامل ويب.
شكل سلسلة التعليمات الرئيسية مع عامل الويب
بعد أن تعرّفت على كيفية استخراج بيانات Exif الوصفية من ملف JPEG على سلسلة التعليمات الرئيسية، إليك كيفية استخراجها عند استخدام Web Worker:
- افتح علامة تبويب أخرى في Chrome، ثم افتح "أدوات مطوّري البرامج" فيها.
- افتح لوحة الأداء.
- انتقِل إلى https://chrome.dev/learn-performance-exif-worker/with-worker.html.
- في لوحة "الأداء"، انقر على زر التسجيل في أعلى يسار جزء "أدوات مطوّري البرامج".
- ألصِق رابط الصورة هذا في الحقل وانقر على الزر الحصول على ملف JPEG.
- بعد ملء الواجهة بالبيانات الوصفية Exif، انقر على زر التسجيل مرة أخرى لإيقاف التسجيل.

هذه هي قوة Web Worker. بدلاً من تنفيذ كل شيء في سلسلة التعليمات الرئيسية، يتم تنفيذ كل العمليات باستثناء تعبئة عارض البيانات الوصفية باستخدام HTML في سلسلة تعليمات منفصلة. وهذا يعني أنّه يتم إتاحة سلسلة التعليمات الرئيسية لتنفيذ مهام أخرى.
ربما تكون الميزة الأكبر هنا هي أنّه على عكس إصدار هذا التطبيق الذي لا يستخدم عامل ويب، لا يتم تحميل النص البرمجي exif-reader
على سلسلة التعليمات الرئيسية، بل على سلسلة تعليمات عامل الويب. وهذا يعني أنّ تكلفة تنزيل النص البرمجي exif-reader
وتحليله وتجميعه تتم خارج سلسلة التعليمات الرئيسية.
والآن، لنتعمّق في رمز 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 الخاص بالصورة إلى عامل الويب. من هناك، يبدأ رمز عامل الويب بعبارة 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 إلى عامل الويب عندما يرسل المستخدم النموذج الذي يتضمّن عنوان URL لملف JPEG.
من هناك، يستخرج جزء الرمز التالي البيانات الوصفية 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 Workers.
ومع ذلك، فإنّ النتائج تستحق الجهد، ولا تقتصر على حالة الاستخدام هذه فقط.
يمكنك استخدام Web Workers في مجموعة متنوعة من المهام، مثل عزل طلبات fetch
ومعالجة الردود ومعالجة كميات كبيرة من البيانات بدون حظر سلسلة التعليمات الرئيسية، وغير ذلك.
عند تحسين أداء تطبيقات الويب، ابدأ بالتفكير في أي شيء يمكن تنفيذه بشكل معقول في سياق عامل الويب. ويمكن أن تكون التحسينات كبيرة، ما يؤدي إلى تحسين تجربة المستخدم بشكل عام على موقعك الإلكتروني.