حتما میدونی که جاواسکریپت زبونی هست که Single Thread هست. یعنی در یک زمان فقط یک کار رو میتونه انجام بده. بنابراین برای اجرا شدنش چندین صف مختلف وجود داره که بتونه هم این کار ها رو مدیریت کنه و هم Performance اش رو حفظ کنه.
ولی حتی تو این حالت ممکنه بعضی اوقات بعضی کار ها پیش بیاد که پیش خودتون بگید کاش حالتی بود که میشد Multi Thread هم داشت. خب در حالت عادی قطعا نمیتونید همچین کاری انجام بدید ولی Worker ها بوجود اومدن که دقیقا برای ما این مشکل رو حل کنن. Worker ها به ما این قابلیت رو میدن که بعضی تسک ها رو بفرستید یه جای دیگه محاسبه بشه و صرفا نتیجه اش برگرده سمت اپلیکیشن ما.
به درد کجا میخوره؟ جایی که محاسبات زیاده! جایی که رد و بدل شدن دیتا بین کلاینت و سرور رو نمیخواید خودتون کنترل کنید. هر کاری که دوست دارید میتونید به کمک Worker ها انجام بدید.
بریم ببینیم چطور میشه از Worker ها استفاده کرد و دقیقا به چه دردی میخورن. سعی میکنم مثال هایی که میزنم تو دنیای واقعی هم بشه استفاده کرد.
همینطور که گفتم ما توی جاواسکریپت یک Thread بیشتر نداریم، بنابراین اگر کار سنگینی بخواد انجام بشه که این Thread اصلی رو متوقف کنه عملا صفحه بی استفاده میشه تا اون کار تموم بشه، مگه اینکه اون تسک رو به صورت Async هندل کنید که باز هم اگه کاری که توی اون تسک میخواد انجام بشه سنگین باشه نتیجه خیلی تفاوتی نمیکنه.
توی تصویر بالا همینطور که میبینید نمودار سبز همون Thread اصلی هست. توی جاواسکریپت همه کار هایی که انجام میدیم روی این Thread انجام میشه. به کمک Worker ها میتونیم یک Thread جداگانه ایجاد کنیم و بعضی کار ها رو روی اون انجام بدیم و نتیجش رو روی Thread اصلی بگیریم و باهاش کار کنیم. یکم شبیه تسک های Async میمونه با چندتا تفاوت.
کاری که Worker براتون انجام میده اینه که وقتی ایجادش میکنید یک Thread جدا ساخته میشه که از Thread اصلی جاواسکریپت جداست، برای ساختن یک Worker میتونیم یه دونه فایل بسازیم، مثلا worker.js و بذاریمش هرجا که دوست داریم، بهتره کنار فایل اصلیمون باشه.
داخل فایل scripts.js برای ایجاد یک worker جدید به این صورت عمل میکنیم:
const worker = new Worker('./worker.js'); console.log(worker);
پس بنابراین نیازی نیست که فایل worker رو داخل صفحه html به صورت یه script قرار بدیم. داخل فایل worker میتونیم هرکاری دلمون میخواد انجام بدیم.
اگر قبلا تجربه کار کردن با iframe رو داشته باشید احتمالا باید با یک event handler به نام آشنا باشید، و برای ارسال اطلاعات به اون صفحه iframe هم از متد postMessage استفاده میکنیم.
دقیقا همون سیستم توی Worker ها هم وجود داره، برای ارسال اطلاعات به فایل worker.js باید توی فایل والد که اینجا همون scripts.js هست از متد postMessage استفاده کنیم و برای دریافت اطلاعات ازش هم از . این ارتباط چون به صورت دو طرفه هندل میشه همین ۲ تا رو توی فایل worker هم داریم.
پس یه چیزی شبیه این میشه:
برای اینکار باید داخل scripts.js همین postMessage و رو داشته باشیم:
const worker = new Worker('./worker.js'); worker.postMessage({ type: 'add', values: [5, 2] }); worker.((e) => { console.log(e.data); });
الان ما یه آبجکت فرستادیم برای worker که داخلش یه type داریم که مقدارش رو add گذاشتیم، بعدا داخل worker میتونیم اینطوری دریافتش کنیم:
const add = (a, b) => a + b; = (e) => { if (e.data.type === 'add') { postMessage(add(...e.data.values)); } }
اینجا ما یه فانکشن داریم به اسم add که فقط ۲ تا عدد میگیره و جمع میکنه و برمیگردونه، توی هم نگاه میکنیم که اگر e.data (هر چیری که میفرستید بعنوان data میره داخل این قسمت از event) مقدار type اش add بود ما نتیجه رو محاسبه میکنیم و به کمک postMessage نتیجه رو میفرستیم به scripts.js. اونطرف هم که console.log کردیم باید روی کنسول عدد ۷ رو ببینیم.
تمام.
استفاده از worker ها به همین سادگیه، ولی چندتا محدودیت و نکته مهم داره که باید بدونیم:
میتونید به هر تعداد که دلتون میخواد worker ایجاد کنید و با هرکدومش یه کار انجام بدید، مثلا یکی باشه برای ارتباط socket، یکی دیگه باشه برای محاسبات سنگین و یکی دیگه هم بذارید برای قشنگی! نکته اینه که تا جایی که من میدونم محدودیت خاصی وجود نداره، ولی نکته ای که هست اینه که اون worker فقط توی فایلی که ساخته شده قابل دسترس هست.
برای حل این موضوع میتونید از نوع دیگری از worker ها استفاده کنید که همه چیزشون با worker های معمولی یکیه با این تفاوت که میتونید بین چندتا فایل به اشتراک گذاشته بشن، اسمشون هم هست SharedWorker. این worker ها بر خلاف ورکر های قبلی میتونن بین صفحات مختلف، بین فایل های مختلف و حتی بین worker های مختلف قابل دسترس باشن. چون خیلی شبیه worker ها هستن من دیگه اشاره ای به نحوه استفاده ازشون نمیکنم و پیشنهاد میدم این لینک رو برید بخونید.
احتمالا قبلا اسم ServiceWorker رو شنیده باشید و حتی استفاده کرده باشید ازشون، برای cache کردن اطلاعات و عموما برای ساختن PWA ها ازشون استفاده میشه، برای اینکه مفاهیم Web Worker ها با Service Worker ها قاطی نشه لازم دونستم که به تفاوت هاش اشاره کنم:
همونطور که تا اینجا گفتم از Worker ها برای کار های Parallel استفاده میشه، یک تسک خیلی سخت رو میخوام بهتون معرفی کنم:
let total = 0; for (let i = 0; i <= 10000000000; i += 1) { total += i; } console.log(total); console.log('MIAD');
اگر این کد رو روی مرورگرتون اجرا کنید (بسته به سیستم تون) ممکنه بین ۵ تا ۳۰ ثانیه جوابش طول بکشه و بعد از اینکه جوابشم آماده شد تازه اون خط آخر که MIAD رو چاپ میکنه. پس عملا تا زمانی که کار این حلقه تموم نشده اجرای باقی کد متوقف میشه. اینجاس که میتونیم از worker برای هندل کردن اینکار استفاده کنیم پس این تسک رو میبریم توی worker:
worker.js
const calc = () => { let total = 0; for (let i = 0; i <= 10000000000; i += 1) { total += i; } return total; }; = (e) => { if (e.data.type === 'calc') { postMessage(calc()); } }
scripts.js
const worker = new Worker('./worker.js'); console.log('1'); worker.postMessage({ type: 'calc' }); worker. = (e) => console.log(e.data); console.log('2');
با این شرایط اگر این کد رو اجرا کنید روی کنسول همچین چیزی میبینید:
این تسک روی یک Thread دیگه انجام شد و خللی تو اجرای Main Thread ایجاد نشد.
این مثال به کمک React و Worker ها انجام شده، فایل های زیاده داره، مثال رو روی گیت قرار دادم که میتونید از اونجا ببینیدش، پیشنهاد میکنم حتما حتما مثال رو ببینید چون علاوه بر کاربرد Worker میتونید ببینید چطوری داخل React ازش میشه استفاده کرد.
دمتون گرم که تا اینجای مطلب رو خوندید، امیدوارم که بتونید به کمک Worker ها کارای خفن تر انجام بدید! اگه دوست داشتید میتونید از طریق لینکدین باهام در ارتباط باشید.