הטמעת Web Workers לחישובי רקע

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

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

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

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
הטמעת Web Workers לחישובי רקע
בינוני
~2-3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1502
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1307
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1050
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

הטמעת Web Workers לחישובי רקע

דמיינו: משתמש מעלה קובץ CSV בגודל 10 MB והממשק קופא למשך 5 שניות. הגלילה נעצרת, הכפתורים לא מגיבים. זה קורה כי JavaScript הוא חד-חוטי וניתוח הקובץ חוסם את ממשק המשתמש. הפתרון הוא העברת חישובים כבדים ל-Web Workers. הניסיון שלנו: 10+ שנים ביישומי אינטרנט בעומס גבוה, 50+ פרויקטים עם Workers, האצת האינטראקציה פי 3–5. צרו קשר — ניישם ריבוי חוטים המותאם לסטack שלכם וללוחות הזמנים.

Web Workers אין להם גישה ל-DOM, window, document. התקשורת עם החוט הראשי היא רק דרך הודעות (postMessage/onmessage). הגבלה זו מגנה מפני תנאי מרוץ. אנו משתמשים בעטיפות טיפוסיות ו-Transferable Objects להעברת נתונים גדולים ביעילות. למידע נוסף על ה-API, ראו תיעוד MDN.

בעיות ש-Web Workers פותרים

  • ניתוח והמרה של קבצי CSV/JSON גדולים (>1 MB) — האצה פי 4 על ידי העברה מהחוט הראשי.
  • הצפנה/פענוח נתונים (AES, RSA) — לוקח 500 אלפיות השנייה על החוט הראשי, ב-Worker — ללא השפעה על ממשק המשתמש.
  • עיבוד גרפיקת Canvas דרך OffscreenCanvas — כל הציור ברקע, החוט הראשי מגיב רק לקלט המשתמש.
  • אלגוריתמי חיפוש ומיון על מערכים גדולים (10⁶+ אלמנטים) — מקביל עם מאגר Workers בשניות.
  • דחיסת נתונים (pako, zlib) — דחיסה לפני שליחה לשרת ללא חסימת הממשק.
  • סימולציות פיזיקה ו-ray tracing — לוויזואליזציות אינטראקטיביות.

כיצד אנו מיישמים Web Workers

בפרויקט אחרון, צמצמנו את זמן ניתוח ה-CSV מ-5 שניות ל-0.8 שניות על ידי העברתו למאגר של 4 Workers, ושיפרנו את ה-First Input Delay ב-80%. עקרונות מרכזיים שאנו פועלים לפיהם:

  • Transferable Objects — העברת ArrayBuffer, ImageBitmap, OffscreenCanvas בהפניה (ללא העתקה) לנתונים גדולים.
  • מאגר Workers — המספר שווה למספר ליבות המעבד, משימות מחולקות דרך תור יחיד.
  • עטיפות טיפוסיות — מבטלות שגיאות טיפוס בחילופי הודעות.
  • טיפול בשגיאות ברמת המתזמן — הפעלה מחדש של Worker שקרס ללא אובדן נתונים.
שיטת העברת נתונים מהירות תמיכה שימוש
Structured clone פי 1 (העתקה) כל הדפדפנים נתונים קטנים (<1MB)
Transferable Objects פי 10+ (ללא העתקה) Chrome, Firefox, Safari 16.4+ ArrayBuffer, ImageBitmap
מידע נוסף על Transferable Objects העברת אובייקטים בהפניה אפשרית עבור ArrayBuffer, ImageBitmap, OffscreenCanvas, ReadableStream, WritableStream. לאחר קריאה ל-postMessage עם מערך transferables, האובייקט המקורי הופך לבלתי נגיש לשולח, מה שמונע מרוצי נתונים. זה קריטי עבור מאגרים מ-10 MB — זמן האחזור יורד עשרות מונים.

מבנה Worker בסיסי ועטיפה טיפוסית

// worker.ts — фоновый поток
self.onmessage = ({ data }: MessageEvent) => {
  const result = heavyComputation(data.payload);
  self.postMessage({ type: 'RESULT', payload: result });
};

// main.ts — основной поток
const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
worker.postMessage({ type: 'PROCESS', payload: largeArray });
worker.onmessage = (event) => console.log('Результат:', event.data.payload);
worker.onerror = (error) => console.error('Worker error:', error.message);
worker.terminate();

עבודה עם postMessage גולמי אינה נוחה. עטיפה טיפוסית פותרת זאת:

type WorkerMessage<T extends Record<string, unknown>> = { [K in keyof T]: { type: K; payload: T[K] } }[keyof T];

interface WorkerRequest {
  SORT: { array: number[]; direction: 'asc' | 'desc' };
  FILTER: { data: Record<string, unknown>[]; query: string };
  PARSE_CSV: { content: string };
}

interface WorkerResponse {
  SORT_DONE: number[];
  FILTER_DONE: Record<string, unknown>[];
  PARSE_CSV_DONE: Record<string, string>[];
  ERROR: { message: string };
}

class TypedWorker {
  private worker: Worker;
  private pending = new Map<string, { resolve: Function; reject: Function }>();
  private seq = 0;

  constructor(workerUrl: URL) {
    this.worker = new Worker(workerUrl, { type: 'module' });
    this.worker.onmessage = ({ data }) => {
      const { id, type, payload } = data;
      const handler = this.pending.get(id);
      if (!handler) return;
      this.pending.delete(id);
      if (type === 'ERROR') handler.reject(new Error(payload.message));
      else handler.resolve(payload);
    };
  }

  send<K extends keyof WorkerRequest>(type: K, payload: WorkerRequest[K]): Promise<WorkerResponse[`${K}_DONE` & keyof WorkerResponse]> {
    return new Promise((resolve, reject) => {
      const id = String(++this.seq);
      this.pending.set(id, { resolve, reject });
      this.worker.postMessage({ id, type, payload });
    });
  }

  terminate() {
    this.worker.terminate();
  }
}

העברת נתונים גדולים ו-OffscreenCanvas

כברירת מחדל, postMessage מעתיק נתונים. עבור ArrayBuffers גדולים, זה יקר. Transferable Objects מעבירים בהפניה (העברת בעלות), ללא העתקה:

const buffer = new ArrayBuffer(1024 * 1024 * 10); // 10 MB
const view = new Float32Array(buffer);
// Заполняем данными...
// Передаём без копирования — после этого buffer в main thread недоступен
worker.postMessage({ type: 'PROCESS', payload: buffer }, [buffer]);
// В Worker
self.onmessage = (event: MessageEvent) => {
    const buf = event.data.payload as ArrayBuffer;
    const view = new Float32Array(buf);
    // обрабатываем...
    ctx.postMessage({ type: 'DONE', payload: buf }, [buf]);
};

Transferable: ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas, ReadableStream, WritableStream.

OffscreenCanvas – עיבוד ב-Worker:

// main.ts
const canvas = document.getElementById('chart') as HTMLCanvasElement;
const offscreen = canvas.transferControlToOffscreen();
worker.postMessage({ type: 'INIT_CANVAS', canvas: offscreen }, [offscreen]);
worker.postMessage({ type: 'RENDER', data: chartData });

// chart-worker.ts
let ctx: OffscreenCanvasRenderingContext2D;
self.onmessage = (event: MessageEvent) => {
    const { type, canvas, data } = event.data;
    if (type === 'INIT_CANVAS') {
        ctx = canvas.getContext('2d')!;
        return;
    }
    if (type === 'RENDER') renderChart(ctx, data);
};

מאגר Workers וסובלנות לתקלות

class WorkerPool {
    private workers: Worker[] = [];
    private queue: Array<{ resolve: Function; reject: Function; message: unknown }> = [];
    private idle: Worker[] = [];

    constructor(workerUrl: URL, poolSize = navigator.hardwareConcurrency || 4) {
        for (let i = 0; i < poolSize; i++) {
            const worker = new Worker(workerUrl, { type: 'module' });
            worker.onmessage = (event) => this.onWorkerMessage(worker, event);
            worker.onerror = (error) => this.onWorkerError(worker, error);
            this.workers.push(worker);
            this.idle.push(worker);
        }
    }

    execute(message: unknown): Promise<unknown> {
        return new Promise((resolve, reject) => {
            const task = { resolve, reject, message };
            const worker = this.idle.pop();
            if (worker) this.dispatch(worker, task);
            else this.queue.push(task);
        });
    }

    private dispatch(worker: Worker, task: { resolve: Function; reject: Function; message: unknown }) {
        (worker as any).__resolve = task.resolve;
        (worker as any).__reject = task.reject;
        worker.postMessage(task.message);
    }

    private onWorkerMessage(worker: Worker, event: MessageEvent) {
        (worker as any).__resolve?.(event.data);
        this.scheduleNext(worker);
    }

    private onWorkerError(worker: Worker, error: ErrorEvent) {
        (worker as any).__reject?.(new Error(error.message));
        this.scheduleNext(worker);
    }

    private scheduleNext(worker: Worker) {
        const next = this.queue.shift();
        if (next) this.dispatch(worker, next);
        else this.idle.push(worker);
    }

    terminate() {
        this.workers.forEach((w) => w.terminate());
    }
}

קריסת Worker לא אמורה להשפיע על היישום הראשי. המתזמן מפעיל מחדש את ה-Worker שנכשל ומחלק מחדש משימות. במאגר, אנו משתמשים בתבנית heartbeat: כל Worker שולח אות כל 10 שניות. אם אין אות במשך 20 שניות, ה-Worker נחשב מת ומוחלף. זה נותן 99.9% זמינות בעומס רגיל.

בהשוואה ל-Worker יחיד, מאגר עם 8 Workers מהיר פי 8 עבור 100 משימות מקבילות (1.2 שניות לעומת 10 שניות). השוואה זו מראה מדוע ביצוע מקביל עם מאגר יעיל הרבה יותר מעיבוד רציף.

פרמטר Worker יחיד מאגר Workers
עיבוד 100 משימות של 100 אלפיות השנייה כל אחת ~10 שניות (רציף) ~1.2 שניות (8 Workers)
סובלנות לתקלות נמוכה (קריסה = אובדן) גבוהה (הפעלה מחדש)
שימוש בזיכרון מינימלי (חוט אחד) בינוני (לכל חוט)

מה כלול ושלבי העבודה

אנו מבטיחים ביקורת יסודית: פרופיילינג, ניתוח Core Web Vitals. תכנון הארכיטקטורה כולל בחירת מספר ה-Workers, תוכנית העברת נתונים, אסטרטגיית גיבוי. היישום מכסה עטיפות טיפוסיות, מאגר, React/Vue hooks. אנו מספקים בדיקות יחידה ותיעוד. לאחר המסירה, חודש של תמיכה ושיפורים. למהנדסים המוסמכים שלנו יש 10+ שנות ניסיון מוכח ביישומי אינטרנט בעומס גבוה.

שלבי העבודה:

  1. ביקורת — פרופיילינג של הביצועים הנוכחיים, זיהוי מועמדים להעברה.
  2. תכנון — בחירת תבנית (Worker יחיד, מאגר, OffscreenCanvas), הגדרת חוזי הודעות.
  3. יישום — כתיבת Workers, אינטגרציה, הוספת טיפוסים.
  4. בדיקות — בדיקות יחידה, בדיקות עומס (סימולציה של 20+ משימות במקביל).
  5. פריסה וניטור — השקה, מעקב אחר LCP/INP לאחר השחרור.

הערכות לוחות זמנים: בין יום ל-5 ימים תלוי במורכבות. החל מ-$500 לאינטגרציה בסיסית. צרו קשר כדי לדון בפרויקט שלכם. יש לנו רקורד מוכח עם יותר מ-50 יישומים מוצלחים, המבטיח שההשקעה שלכם בטוחה.

למה לבחור בנו

אנחנו צוות מהנדסים עם 10+ שנות ניסיון בפיתוח web. השלמנו 50+ פרויקטים עם Web Workers — מניתוח לוגים בצד הלקוח ועד לוחות מחוונים בזמן אמת עם OffscreenCanvas. הפתרונות שלנו מאיצים חישובים פי 3–5 מבלי לפגוע ביציבות. צרו קשר — נעריך את הפרויקט שלכם ונספק לוחות זמנים בין יום ל-5 ימים תלוי במורכבות.