Web Workers: איך מריצים קוד כבד בלי לתקוע את הדפדפן

מאת צוות מדיה דיל · 04.09.2026 · טכנולוגיה · 6 דק׳

למה חישוב כבד תוקע את הדפדפן, מודל ה-Thread הנפרד, postMessage ו-Transferable Objects, ומתי Web Worker משתלם לעומת Concurrent Rendering.

JavaScript בדפדפן רץ על Thread יחיד — כל חישוב כבד, מיון מערך ענק, או עיבוד תמונה בצד לקוח חוסם את אותו Thread שאחראי גם על רינדור הממשק. התוצאה: הדפדפן "קופא", לחיצות לא מגיבות, וגלילה נתקעת, גם אם השרת מהיר לחלוטין. Web Workers פותרים את זה על ידי הרצת קוד על Thread נפרד לגמרי.

ה-Main Thread כמשאב יחיד ומשותף

כל דבר שהמשתמש רואה ומרגיש — רינדור, אנימציות, תגובה ללחיצות — קורה על אותו Thread יחיד שגם מריץ את קוד ה-JavaScript של האפליקציה. Loop כבד שרץ 200 מילישניות על ה-Main Thread חוסם את הדפדפן מלהגיב לשום דבר אחר באותו חלון זמן, כולל אנימציה חלקה שהתחילה רגע קודם.

מה Web Worker בעצם נותן: Thread נפרד עם הקשר משלו

Worker רץ בהקשר גלובלי נפרד לגמרי — אין לו גישה ישירה ל-DOM, ל-window, או למשתני ה-Main Thread. זו לא מגבלה שרירותית אלא ערובה לבטיחות: קוד שרץ ב-Worker לא יכול לגעת בממשק בטעות ולגרום לבאגים של מרוץ תהליכים (Race Conditions) על אותו מבנה נתונים משותף.

postMessage: איך שני ה-Threads בכלל מדברים

התקשורת בין Main Thread ל-Worker קורית אך ורק דרך postMessage — הודעות אסינכרוניות שמועברות בין השניים, לא זיכרון משותף. הנתונים מועתקים (Structured Clone) בכל העברה כברירת מחדל, לא מועברים בהפניה, כך שאי אפשר לשנות נתון משותף בטעות משני צדדים בו-זמנית.

Transferable Objects: העברה בלי העתקה

העתקת נתון ענק — למשל ArrayBuffer של תמונה גולמית — בכל postMessage יכולה להיות יקרה בעצמה. Transferable Objects מעבירים בעלות על הזיכרון עצמו ל-Worker במקום להעתיק אותו, כמעט חינם מבחינת ביצועים, אבל ה-Thread המקורי מאבד גישה לנתון לגמרי אחרי ההעברה — טרייד-אוף שצריך לתכנן סביבו במבנה הקוד.

מתי Web Worker באמת משתלם

עיבוד נתונים כבד — פענוח קובץ גדול, חישוב Hash, מיון מבנה ענק, עיבוד תמונה או וידאו בצד לקוח — הם המקרים הקלאסיים. אם החישוב לוקח פחות מכמה מילישניות, התקורה של יצירת Worker והעברת הודעות עולה על התועלת; Worker משתלם כשהחסימה בפועל מורגשת למשתמש.

Worker לא פותר הכל: לא Real DOM Access

טעות נפוצה היא לצפות ש-Worker "יעזור" בעדכון ממשק — הוא לא יכול לגעת ב-DOM בכלל. השימוש הנכון הוא שה-Worker מבצע את החישוב הכבד ומחזיר תוצאה סופית קטנה יחסית ל-Main Thread, שממנה ה-Main Thread כבר מעדכן את הממשק בעצמו.

React Concurrent Rendering: פתרון משלים, לא תחליף

Concurrent Rendering ב-React פותר בעיה דומה במהות אבל שונה בשכבה — הוא מאפשר לתזמן עדכוני ממשק בעדיפות שונה על אותו Thread יחיד, לא מעביר עבודה ל-Thread נפרד. השילוב הנכון: Web Worker לחישוב כבד אמיתי, Concurrent Rendering לתעדוף עדכוני ממשק על אותו Thread.

Shared Workers ו-Service Workers: משפחה רחבה יותר

Web Worker רגיל שייך לטאב בודד ונעלם כשהוא נסגר. Shared Worker יכול לשרת כמה טאבים באותו מקור בו-זמנית, שימושי לשיתוף חיבור או מצב בין כמה חלונות פתוחים של אותה אפליקציה. Service Worker הוא משפחה נפרדת לגמרי, ממוקדת ביירוט בקשות רשת ותמיכה ב-Offline, לא בחישוב כבד — כדאי לא לבלבל בין השלושה למרות השם הדומה.

עלות היצירה: Worker לא בחינם להקים

יצירת Worker חדש דורשת זמן — טעינת קובץ, אתחול הקשר גלובלי נפרד — לרוב כמה מילישניות עד עשרות מילישניות, תלוי בגודל הקוד שהוא טוען. תבנית נפוצה בעומסי עבודה חוזרים היא Worker Pool: מספר קבוע של Workers שנשארים חיים ומקבלים משימות חדשות דרך תור פנימי, במקום ליצור ולהרוס Worker חדש בכל קריאה.

הדפדפן שלכם נתקע בזמן חישובים כבדים בצד לקוח? נשמח לעזור לכם להעביר אותם ל-Thread נפרד בוואטסאפ.

תגיות: Web Workers · JavaScript · Browser Performance · Frontend

← חזרה לבלוג · צור קשר