Skip to content

Worker يتيح لك بدء والتواصل مع مثيل JavaScript جديد يعمل على سلسلة منفصلة مع مشاركة موارد I/O مع السلسلة الرئيسية.

يطبق Bun نسخة دنيا من Web Workers API مع إضافات تجعله يعمل بشكل أفضل لحالات الاستخدام من جانب الخادم. مثل بقية Bun، Worker في Bun يدعم CommonJS و ES Modules و TypeScript و JSX و TSX وأكثر من ذلك دون الحاجة إلى خطوات بناء إضافية.

إنشاء Worker

كما في المتصفحات، Worker هو global. استخدمه لإنشاء سلسلة worker جديدة.

من السلسلة الرئيسية

ts
const worker = new Worker("./worker.ts");

worker.postMessage("hello");
worker.onmessage = event => {
  console.log(event.data);
};

سلسلة Worker

ts
// يمنع أخطاء TS
declare var self: Worker;

self.onmessage = (event: MessageEvent) => {
  console.log(event.data);
  postMessage("world");
};

لمنع أخطاء TypeScript عند استخدام self، أضف هذا السطر إلى أعلى ملف worker الخاص بك.

ts
declare var self: Worker;

يمكنك استخدام صيغة import و export في كود worker الخاص بك. على عكس المتصفحات، لا حاجة لتحديد {type: "module"} لاستخدام ES Modules.

لتبسيط معالجة الأخطاء، يتم حل النص الأولي للتحميل عند استدعاء new Worker(url).

js
const worker = new Worker("/not-found.js");
// يرمي خطأ على الفور

المحدد الذي يتم تمريره إلى Worker يتم حله بالنسبة لجذر المشروع (مثل كتابة bun ./path/to/file.js).

preload - تحميل الوحدات قبل بدء worker

يمكنك تمرير مصفوفة من محددات الوحدات إلى خيار preload لتحميل الوحدات قبل بدء worker. هذا مفيد عندما تريد التأكد من تحميل بعض الكود دائمًا قبل بدء التطبيق، مثل تحميل OpenTelemetry أو Sentry أو DataDog أو غيرها.

ts
const worker = new Worker("./worker.ts", {
  preload: ["./load-sentry.js"],
});

مثل حجة CLI --preload، يتم معالجة خيار preload قبل بدء worker.

يمكنك أيضًا تمرير سلسلة واحدة إلى خيار preload:

ts
const worker = new Worker("./worker.ts", {
  preload: "./load-sentry.js",
});

عناوين blob:

يمكنك أيضًا تمرير عنوان blob: إلى Worker. هذا مفيد لإنشاء workers من السلاسل أو المصادر الأخرى.

js
const blob = new Blob([`self.onmessage = (event: MessageEvent) => postMessage(event.data)`], {
  type: "application/typescript",
});
const url = URL.createObjectURL(blob);
const worker = new Worker(url);

مثل بقية Bun، workers المنشأة من عناوين blob: تدعم TypeScript و JSX وأنواع الملفات الأخرى دون الحاجة إلى خطوات بناء إضافية. يمكنك التواصل أنه يجب تحميلها عبر TypeScript إما عبر type أو بتمرير filename إلى منشئ File.

ts
const file = new File([`self.onmessage = (event: MessageEvent) => postMessage(event.data)`], "worker.ts");
const url = URL.createObjectURL(file);
const worker = new Worker(url);

"open"

يتم إصدار الحدث "open" عند إنشاء worker وجاهز لاستقبال الرسائل. يمكن استخدام هذا لإرسال رسالة أولية إلى worker بمجرد جاهزيته. (هذا الحدث غير موجود في المتصفحات.)

ts
const worker = new Worker(new URL("worker.ts", import.meta.url).href);

worker.addEventListener("open", () => {
  console.log("worker is ready");
});

يتم وضع الرسائل في قائمة الانتظار تلقائيًا حتى يصبح worker جاهزًا، لذا لا حاجة للانتظار لحدث "open" لإرسال الرسائل.

الرسائل مع postMessage

لإرسال الرسائل، استخدم worker.postMessage و self.postMessage. هذا يستفيد من خوارزمية الاستنساخ الهيكلي HTML.

تحسينات الأداء

يتضمن Bun مسارات سريعة محسنة لـ postMessage لتحسين الأداء بشكل كبير لأنواع البيانات الشائعة:

مسار سريع للسلسلة - عند نشر قيم سلسلة نقية، يتجاوز Bun خوارزمية الاستنساخ الهيكلي تمامًا، محققًا مكاسب أداء كبيرة بدون أي نفقات عامة للتسلسل.

مسار سريع للكائنات البسيطة - للكائنات العادية التي تحتوي فقط على قيم بدائية (سلاسل، أرقام، قيم منطقية، null، undefined)، يستخدم Bun مسار تسلسل محسن يخزن الخصائص مباشرة بدون استنساخ هيكلي كامل.

يتم تفعيل مسار الكائنات البسيطة السريع عندما يكون الكائن:

  • كائن عادي بدون تعديلات في سلسلة النموذج الأولي
  • يحتوي فقط على خصائص بيانات قابلة للعد والتكوين
  • لا يحتوي على خصائص مفهرسة أو دوال getter/setter
  • جميع قيم الخصائص هي بدائية أو سلاسل

مع هذه المسارات السريعة، أداء postMessage في Bun أسرع بـ 2-241 مرة لأن طول الرسالة لم يعد له تأثير كبير على الأداء.

Bun (مع المسارات السريعة):

ts
postMessage({ prop: سلسلة 11 حرف، ...9 خصائص أخرى }) - 648ns
postMessage({ prop: سلسلة 14 KB، ...9 خصائص أخرى })    - 719ns
postMessage({ prop: سلسلة 3 MB، ...9 خصائص أخرى })     - 1.26µs

Node.js v24.6.0 (للمقارنة):

js
postMessage({ prop: سلسلة 11 حرف، ...9 خصائص أخرى }) - 1.19µs
postMessage({ prop: سلسلة 14 KB، ...9 خصائص أخرى })    - 2.69µs
postMessage({ prop: سلسلة 3 MB، ...9 خصائص أخرى })     - 304µs
js
// مسار سريع للسلسلة - محسن
postMessage("Hello, worker!");

// مسار سريع للكائنات البسيطة - محسن
postMessage({
  message: "Hello",
  count: 42,
  enabled: true,
  data: null,
});

// الكائنات المعقدة لا تزال تعمل لكن تستخدم استنساخ هيكلي قياسي
postMessage({
  nested: { deep: { object: true } },
  date: new Date(),
  buffer: new ArrayBuffer(8),
});
js
// على سلسلة worker، يتم "توجيه" `postMessage` تلقائيًا إلى السلسلة الأصلية.
postMessage({ hello: "world" });

// على السلسلة الرئيسية
worker.postMessage({ hello: "world" });

لاستقبال الرسائل، استخدم معالج حدث message على سلسلة worker والسلسلة الرئيسية.

js
// سلسلة Worker:
self.addEventListener("message", event => {
  console.log(event.data);
});
// أو استخدم المعين:
// self.onmessage = fn

// إذا على السلسلة الرئيسية
worker.addEventListener("message", event => {
  console.log(event.data);
});
// أو استخدم المعين:
// worker.onmessage = fn

إنهاء worker

ينتهي مثيل Worker تلقائيًا بمجرد عدم وجود عمل متبقي في حلقة الحدث الخاصة به. إرفاق مستمع "message" على global أو أي MessagePort سيبقي حلقة الحدث نشطة. لإنهاء Worker بالقوة، استدعِ worker.terminate().

ts
const worker = new Worker(new URL("worker.ts", import.meta.url).href);

// ...بعد بعض الوقت
worker.terminate();

هذا سيتسبب في خروج worker في أقرب وقت ممكن.

process.exit()

يمكن للـ worker إنهاء نفسه مع process.exit(). هذا لا ينهي العملية الرئيسية. كما في Node.js، يتم إصدار process.on('beforeExit', callback) و process.on('exit', callback) على سلسلة worker (وليس على السلسلة الرئيسية)، ويتم تمرير رمز الخروج إلى حدث "close".

"close"

يتم إصدار الحدث "close" عند إنهاء worker. قد يستغرق الأمر بعض الوقت لينتهي worker فعليًا، لذا يتم إصدار هذا الحدث عند وضع علامة على worker كمنتهي. سيحتوي CloseEvent على رمز الخروج الذي تم تمريره إلى process.exit()، أو 0 إذا تم الإغلاق لأسباب أخرى.

ts
const worker = new Worker(new URL("worker.ts", import.meta.url).href);

worker.addEventListener("close", event => {
  console.log("worker is being closed");
});

هذا الحدث غير موجود في المتصفحات.

إدارة العمر الافتراضي

افتراضيًا، سيبقي Worker النشط العملية الرئيسية (الوالدة) نشطة، لذا فإن المهام غير المتزامنة مثل setTimeout والوعود ستبقي العملية نشطة. إرفاق مستمعي message سيبقي Worker نشطًا أيضًا.

worker.unref()

لإيقاف worker قيد التشغيل من إبقاء العملية نشطة، استدعِ worker.unref(). هذا يفصل بين عمر worker وعمر العملية الرئيسية، ويعادل ما يفعله worker_threads في Node.js.

ts
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.unref();

ملاحظة: worker.unref() غير متوفر في المتصفحات.

worker.ref()

لإبقاء العملية نشطة حتى ينتهي Worker، استدعِ worker.ref(). الـ worker المراجع هو السلوك الافتراضي، ولا يزال يحتاج إلى شيء يحدث في حلقة الحدث (مثل مستمع "message") لاستمرار worker في التشغيل.

ts
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.unref();
// لاحقًا...
worker.ref();

بدلاً من ذلك، يمكنك أيضًا تمرير كائن options إلى Worker:

ts
const worker = new Worker(new URL("worker.ts", import.meta.url).href, {
  ref: false,
});

ملاحظة: worker.ref() غير متوفر في المتصفحات.

استخدام الذاكرة مع smol

يمكن لمثيلات JavaScript استخدام الكثير من الذاكرة. يدعم Worker في Bun وضع smol الذي يقلل استخدام الذاكرة، على حساب الأداء. لتمكين وضع smol، مرر smol: true إلى كائن options في منشئ Worker.

ts
const worker = new Worker("./i-am-smol.ts", {
  smol: true,
});

ماذا يفعل وضع smol فعليًا؟">

تعيين smol: true يضبط JSC::HeapSize ليكون Small بدلاً من Large الافتراضي.

بيانات البيئة

مشاركة البيانات بين السلسلة الرئيسية و workers باستخدام setEnvironmentData() و getEnvironmentData().

index.ts
ts
import { setEnvironmentData, getEnvironmentData } from "worker_threads";

// في السلسلة الرئيسية
setEnvironmentData("config", { apiUrl: "https://api.example.com" });

// في worker
const config = getEnvironmentData("config");
console.log(config); // => { apiUrl: "https://api.example.com" }

أحداث Worker

الاستماع لأحداث إنشاء worker باستخدام process.emit():

index.ts
ts
process.on("worker", worker => {
  console.log("New worker created:", worker.threadId);
});

Bun.isMainThread

يمكنك التحقق مما إذا كنت في السلسلة الرئيسية عن طريق التحقق من Bun.isMainThread.

ts
if (Bun.isMainThread) {
  console.log("I'm the main thread");
} else {
  console.log("I'm in a worker");
}

هذا مفيد لتشغيل الكود شرطيًا بناءً على ما إذا كنت في السلسلة الرئيسية أم لا.

Bun بواسطة www.bunjs.com.cn تحرير