مقدمه

در درس قبل با مفهوم کلی Workerها آشنا شدیم. Dedicated Worker یا «ورکر اختصاصی»، همانطور که از نامش پیداست، یک ریسمان پس‌زمینه است که به صورت اختصاصی توسط یک اسکریپت واحد ایجاد شده و تنها با همان اسکریپت می‌تواند ارتباط برقرار کند. این رایج‌ترین و ساده‌ترین نوع Worker است و برای زمانی که می‌خواهید یک وظیفه (task) مشخص را از ریسمان اصلی جدا کنید، انتخاب ایده‌آلی است.

ایجاد Worker و برقراری ارتباط

استفاده از یک Dedicated Worker نیازمند دو فایل جاوااسکریپت است: یکی اسکریپت اصلی شما (که در صفحه HTML بارگذاری می‌شود) و دیگری، فایل اسکریپت مربوط به خود Worker که قرار است در پس‌زمینه اجرا شود.

۱. اسکریپت اصلی (Main Script)

در اسکریپت اصلی، شما وظیفه ایجاد Worker و مدیریت ارتباط با آن را بر عهده دارید.

  • ایجاد Worker: با استفاده از سازنده new Worker() و دادن آدرس فایل اسکریپت Worker، یک نمونه جدید از آن می‌سازید: const myWorker = new Worker('worker.js';)
  • ارسال پیام به Worker: برای ارسال داده به Worker، از متد myWorker.postMessage(data) استفاده می‌کنید.
  • دریافت پیام از Worker: برای دریافت نتایج از Worker، یک شنونده برای رویداد message روی شیء Worker ثبت می‌کنید: myWorker.onmessage = function(event) { ... }. داده‌ی ارسال شده در event.data قرار دارد.

۲. اسکریپت ورکر (Worker Script)

فایل اسکریپت Worker در محیط ایزوله خود اجرا می‌شود و وظیفه انجام کار سنگین را دارد.

  • دریافت پیام از اسکریپت اصلی: در داخل Worker، با ثبت یک شنونده برای رویداد message روی شیء سراسری self، به پیام‌های ارسالی از ریسمان اصلی گوش می‌دهید: self.onmessage = function(event) { ... }.
  • ارسال پیام به اسکریپت اصلی: پس از انجام محاسبات، Worker نتیجه را با استفاده از متد سراسری postMessage(result) به ریسمان اصلی بازمی‌گرداند.

مثال عملی: یک محاسبه سنگین

بیایید این مفاهیم را با یک مثال واقعی بررسی کنیم. فرض کنید می‌خواهیم یک عدد بزرگ در دنباله فیبوناچی را محاسبه کنیم. این یک عملیات بازگشتی و بسیار سنگین است که اگر در ریسمان اصلی اجرا شود، قطعاً رابط کاربری را برای مدتی قفل خواهد کرد. ما این محاسبه را به یک Worker واگذار می‌کنیم.

Copy Icon JAVASCRIPT - a file named fib-worker.js
// This function is CPU-intensive
function fibonacci(n) {
    if (n < 2) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// Listen for messages from the main thread
self.onmessage = (event) => {
    console.log('Worker received number:', event.data);
    
    // Perform the heavy calculation
    const result = fibonacci(event.data);
    
    // Send the result back
    self.postMessage(result);
};

این فایل fib-worker.js است. این اسکریپت منتظر دریافت یک پیام (که حاوی عدد مورد نظر است) می‌ماند، تابع سنگین fibonacci را اجرا کرده و نتیجه را به ریسمان اصلی بازمی‌گرداند.

Copy Icon JAVASCRIPT - a file named main.js
const numberInput = document.getElementById('fib-input');
const startBtn = document.getElementById('start-btn');
const resultDiv = document.getElementById('result');

// 1. Create the worker
const fibWorker = new Worker('fib-worker.js');

// 2. Send data to the worker on button click
startBtn.addEventListener('click', () => {
    resultDiv.textContent = 'Calculating...';
    fibWorker.postMessage(Number(numberInput.value));
});

// 3. Listen for the result from the worker
fibWorker.onmessage = (event) => {
    resultDiv.textContent = 'Result: ' + event.data;
};

این کد مربوط به اسکریپت اصلی است. ابتدا یک نمونه از Worker می‌سازد. با کلیک روی دکمه، عدد را از فیلد ورودی گرفته و به Worker ارسال می‌کند. سپس با ثبت شنونده onmessage، منتظر دریافت نتیجه می‌ماند و به محض دریافت، آن را در صفحه نمایش می‌دهد. در تمام مدت محاسبه، رابط کاربری کاملاً روان و پاسخ‌گو باقی می‌ماند.

مدیریت خطا و پایان دادن به Worker

برای مدیریت خطاهایی که ممکن است در داخل Worker رخ دهد، می‌توانید در اسکریپت اصلی به رویداد error روی شیء Worker گوش دهید. همچنین، برای متوقف کردن اجباری یک Worker از ریسمان اصلی، می‌توانید متد worker.terminate() را فراخوانی کنید. این کار ریسمان Worker را بلافاصله از بین می‌برد.

Copy Icon JAVASCRIPT - in main.js
fibWorker.onerror = (event) => {
    console.error('An error occurred in the worker!');
    console.error(`Filename: ${event.filename}, Line: ${event.lineno}, Message: ${event.message}`);
    resultDiv.textContent = 'An error occurred in calculation.';
};

// To stop the worker at any time, for example with a cancel button
// cancelButton.addEventListener('click', () => fibWorker.terminate());

در این درس به صورت عملی یاد گرفتیم که چگونه با استفاده از یک Dedicated Worker، وظایف سنگین را به پس‌زمینه منتقل کرده و از پاسخ‌گو بودن رابط کاربری اطمینان حاصل کنیم. این رایج‌ترین الگوی استفاده از Workerها است و یک ابزار کلیدی برای بهینه‌سازی عملکرد اپلیکیشن‌های وب پیچیده محسوب می‌شود. در درس بعدی، به سراغ «کار با Shared Workers» خواهیم رفت و یاد می‌گیریم که چگونه یک Worker واحد می‌تواند بین چندین تب یا پنجره مختلف به اشتراک گذاشته شود.