تنفيذ عملية (Bun.spawn())
قدم أمرًا كمصفوفة من السلاسل. نتيجة Bun.spawn() هي كائن Bun.Subprocess.
const proc = Bun.spawn(["bun", "--version"]);
console.log(await proc.exited); // 0الوسيط الثاني لـ Bun.spawn هو كائن معلمات يمكن استخدامه لتكوين العملية الفرعية.
const proc = Bun.spawn(["bun", "--version"], {
cwd: "./path/to/subdir", // تحديد دليل العمل
env: { ...process.env, FOO: "bar" }, // تحديد متغيرات البيئة
onExit(proc, exitCode, signalCode, error) {
// معالج الخروج
},
});
proc.pid; // معرف العملية للعملية الفرعيةتيار الإدخال
بشكل افتراضي، تيار الإدخال للعملية الفرعية غير معرّف؛ يمكن تكوينه باستخدام المعلمة stdin.
const proc = Bun.spawn(["cat"], {
stdin: await fetch("https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js"),
});
const text = await proc.stdout.text();
console.log(text); // "const input = "hello world".repeat(400); ..."| القيمة | الوصف |
|---|---|
null | افتراضي. عدم تقديم أي إدخال للعملية الفرعية |
"pipe" | إرجاع FileSink للكتابة التدريجية السريعة |
"inherit" | توريث stdin من العملية الأصل |
Bun.file() | القراءة من الملف المحدد |
TypedArray | DataView | استخدام مخزن مؤقت ثنائي كإدخال |
Response | استخدام body للاستجابة كإدخال |
Request | استخدام body للطلب كإدخال |
ReadableStream | استخدام تيار قابل للقراءة كإدخال |
Blob | استخدام blob كإدخال |
number | القراءة من الملف باستخدام واصف ملف معين |
خيار "pipe" يسمح بالكتابة تدريجيًا إلى تيار إدخال العملية الفرعية من العملية الأصل.
const proc = Bun.spawn(["cat"], {
stdin: "pipe", // إرجاع FileSink للكتابة
});
// إضافة بيانات سلسلة
proc.stdin.write("hello");
// إضافة بيانات ثنائية
const enc = new TextEncoder();
proc.stdin.write(enc.encode(" world!"));
// إرسال البيانات المخزنة مؤقتًا
proc.stdin.flush();
// إغلاق تيار الإدخال
proc.stdin.end();تمرير ReadableStream إلى stdin يسمح لك بتوجيه البيانات من ReadableStream في JavaScript مباشرة إلى إدخال العملية الفرعية:
const stream = new ReadableStream({
start(controller) {
controller.enqueue("Hello from ");
controller.enqueue("ReadableStream!");
controller.close();
},
});
const proc = Bun.spawn(["cat"], {
stdin: stream,
stdout: "pipe",
});
const output = await proc.stdout.text();
console.log(output); // "Hello from ReadableStream!"تيارات الإخراج
يمكنك قراءة النتائج من العملية الفرعية عبر الخصائص stdout و stderr. بشكل افتراضي هذه هي مثيلات ReadableStream.
const proc = Bun.spawn(["bun", "--version"]);
const text = await proc.stdout.text();
console.log(text); // => "1.3.3\n"قم بتكوين تيار الإخراج بتمرير إحدى القيم التالية إلى stdout/stderr:
| القيمة | الوصف |
|---|---|
"pipe" | افتراضي لـ stdout. توجيه الإخراج إلى ReadableStream على كائن Subprocess المُرجع |
"inherit" | افتراضي لـ stderr. التوريث من العملية الأصل |
"ignore" | تجاهل الإخراج |
Bun.file() | الكتابة إلى الملف المحدد |
number | الكتابة إلى الملف باستخدام واصف الملف المحدد |
معالجة الخروج
استخدم استدعاء onExit للاستماع عند خروج العملية أو قتلها.
const proc = Bun.spawn(["bun", "--version"], {
onExit(proc, exitCode, signalCode, error) {
// معالج الخروج
},
});للراحة، خاصية exited هي Promise يتم حلها عند خروج العملية.
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited; // يتم الحل عند خروج العملية
proc.killed; // boolean — هل تم قتل العملية؟
proc.exitCode; // null | number
proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...لقتل عملية:
const proc = Bun.spawn(["bun", "--version"]);
proc.kill();
proc.killed; // true
proc.kill(15); // تحديد رمز إشارة
proc.kill("SIGTERM"); // تحديد اسم إشارةلن تنتهي العملية الأصلية bun حتى تخرج جميع العمليات الفرعية. استخدم proc.unref() لفصل العملية الفرعية عن الأصل.
const proc = Bun.spawn(["bun", "--version"]);
proc.unref();استخدام الموارد
يمكنك الحصول على معلومات حول استخدام موارد العملية بعد خروجها:
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited;
const usage = proc.resourceUsage();
console.log(`Max memory used: ${usage.maxRSS} bytes`);
console.log(`CPU time (user): ${usage.cpuTime.user} µs`);
console.log(`CPU time (system): ${usage.cpuTime.system} µs`);استخدام AbortSignal
يمكنك إلغاء عملية فرعية باستخدام AbortSignal:
const controller = new AbortController();
const { signal } = controller;
const proc = Bun.spawn({
cmd: ["sleep", "100"],
signal,
});
// لاحقًا، لإلغاء العملية:
controller.abort();استخدام timeout و killSignal
يمكنك تعيين مهلة زمنية للعملية الفرعية لإنهائها تلقائيًا بعد مدة محددة:
// قتل العملية بعد 5 ثوانٍ
const proc = Bun.spawn({
cmd: ["sleep", "10"],
timeout: 5000, // 5 ثوانٍ بالمللي ثانية
});
await proc.exited; // سيتم الحل بعد 5 ثوانٍبشكل افتراضي، يتم قتل العمليات التي انتهت مهلتها باستخدام إشارة SIGTERM. يمكنك تحديد إشارة مختلفة باستخدام خيار killSignal:
// قتل العملية باستخدام SIGKILL بعد 5 ثوانٍ
const proc = Bun.spawn({
cmd: ["sleep", "10"],
timeout: 5000,
killSignal: "SIGKILL", // يمكن أن يكون اسم سلسلة أو رقم إشارة
});خيار killSignal يتحكم أيضًا في الإشارة المرسلة عند إلغاء AbortSignal.
استخدام maxBuffer
بالنسبة لـ spawnSync، يمكنك الحد من الحد الأقصى لعدد بايتات الإخراج قبل قتل العملية:
// قتل 'yes' بعد إصدار أكثر من 100 بايت من الإخراج
const result = Bun.spawnSync({
cmd: ["yes"], // أو ["bun", "exec", "yes"] على Windows
maxBuffer: 100,
});
// العملية تخرجالاتصال بين العمليات (IPC)
يدعم Bun قناة اتصال مباشرة بين العمليات بين عمليتي bun. لاستلام الرسائل من عملية فرعية Bun مُنفَّذة، حدد معالج ipc.
const child = Bun.spawn(["bun", "child.ts"], {
ipc(message) {
/**
* الرسالة المستلمة من العملية الفرعية
**/
},
});يمكن للعملية الأصلية إرسال رسائل إلى العملية الفرعية باستخدام الدالة .send() على مثيل Subprocess المُرجع. مرجع إلى العملية الفرعية المرسلة متاح أيضًا كوسيط ثاني في معالج ipc.
const childProc = Bun.spawn(["bun", "child.ts"], {
ipc(message, childProc) {
/**
* الرسالة المستلمة من العملية الفرعية
**/
childProc.send("Respond to child");
},
});
childProc.send("I am your father"); // يمكن للأصل إرسال رسائل إلى الابن أيضًافي هذه الأثناء، يمكن للعملية الفرعية إرسال رسائل إلى أصلها باستخدام process.send() واستلام الرسائل مع process.on("message"). هذه هي نفس واجهة برمجة التطبيقات المستخدمة لـ child_process.fork() في Node.js.
process.send("Hello from child as string");
process.send({ message: "Hello from child as object" });
process.on("message", message => {
// طباعة الرسالة من الأصل
console.log(message);
});// إرسال سلسلة
process.send("Hello from child as string");
// إرسال كائن
process.send({ message: "Hello from child as object" });خيار serialization يتحكم في تنسيق الاتصال الأساسي بين العمليتين:
advanced: (افتراضي) يتم تسلسل الرسائل باستخدام واجهة برمجة تطبيقات JSCserialize، التي تدعم استنساخ كل ما يدعمهstructuredClone. هذا لا يدعم نقل ملكية الكائنات.json: يتم تسلسل الرسائل باستخدامJSON.stringifyوJSON.parse، التي لا تدعم العديد من أنواع الكائنات مثلadvanced.
لفصل قناة IPC من العملية الأصلية، اتصل بـ:
childProc.disconnect();IPC بين Bun و Node.js
لاستخدام IPC بين عملية bun وعملية Node.js، اضبط serialization: "json" في Bun.spawn. هذا لأن Node.js و Bun يستخدمان محركات JavaScript مختلفة مع تنسيقات تسلسل كائنات مختلفة.
if (typeof Bun !== "undefined") {
const prefix = `[bun ${process.versions.bun} 🐇]`;
const node = Bun.spawn({
cmd: ["node", __filename],
ipc({ message }) {
console.log(message);
node.send({ message: `${prefix} 👋 hey node` });
node.kill();
},
stdio: ["inherit", "inherit", "inherit"],
serialization: "json",
});
node.send({ message: `${prefix} 👋 hey node` });
} else {
const prefix = `[node ${process.version}]`;
process.on("message", ({ message }) => {
console.log(message);
process.send({ message: `${prefix} 👋 hey bun` });
});
}واجهة برمجة التطبيقات المحظورة (Bun.spawnSync())
يوفر Bun مرادفًا متزامنًا لـ Bun.spawn يسمى Bun.spawnSync. هذه هي واجهة برمجة التطبيقات المحظورة التي تدعم نفس المدخلات والمعلمات مثل Bun.spawn. تُرجع كائن SyncSubprocess، الذي يختلف عن Subprocess في بعض النواحي.
- يحتوي على خاصية
successتشير إلى ما إذا كانت العملية قد خرجت برمز خروج صفر. - خصائص
stdoutوstderrهي مثيلاتBufferبدلاً منReadableStream. - لا توجد خاصية
stdin. استخدمBun.spawnللكتابة تدريجيًا إلى تيار إدخال العملية الفرعية.
const proc = Bun.spawnSync(["echo", "hello"]);
console.log(proc.stdout.toString());
// => "hello\n"كقاعدة عامة، واجهة برمجة التطبيقات غير المتزامنة Bun.spawn أفضل لخوادم HTTP والتطبيقات، و Bun.spawnSync أفضل لبناء أدوات سطر الأوامر.
معايير الأداء
NOTE
⚡️ تحت الغطاء، يستخدم `Bun.spawn` و `Bun.spawnSync` [`posix_spawn(3)`](https://man7.org/linux/man-pages/man3/posix_spawn.3.html).تنفذ spawnSync الخاصة بـ Bun العمليات أسرع بنسبة 60٪ من وحدة child_process في Node.js.
bun spawn.mjscpu: Apple M1 Max
runtime: bun 1.x (arm64-darwin)
benchmark time (avg) (min … max) p75 p99 p995
--------------------------------------------------------- -----------------------------
spawnSync echo hi 888.14 µs/iter (821.83 µs … 1.2 ms) 905.92 µs 1 ms 1.03 msnode spawn.node.mjscpu: Apple M1 Max
runtime: node v18.9.1 (arm64-darwin)
benchmark time (avg) (min … max) p75 p99 p995
--------------------------------------------------------- -----------------------------
spawnSync echo hi 1.47 ms/iter (1.14 ms … 2.64 ms) 1.57 ms 2.37 ms 2.52 msمرجع
يُظهر أدناه مرجع لواجهة برمجة تطبيقات Spawn والأنواع. الأنواع الحقيقية تحتوي على generics معقدة لنوع تيارات Subprocess بقوة مع الخيارات الممررة إلى Bun.spawn و Bun.spawnSync. للحصول على التفاصيل الكاملة، ابحث عن هذه الأنواع كما هي معرّفة في bun.d.ts.
interface Bun {
spawn(command: string[], options?: SpawnOptions.OptionsObject): Subprocess;
spawnSync(command: string[], options?: SpawnOptions.OptionsObject): SyncSubprocess;
spawn(options: { cmd: string[] } & SpawnOptions.OptionsObject): Subprocess;
spawnSync(options: { cmd: string[] } & SpawnOptions.OptionsObject): SyncSubprocess;
}
namespace SpawnOptions {
interface OptionsObject {
cwd?: string;
env?: Record<string, string | undefined>;
stdio?: [Writable, Readable, Readable];
stdin?: Writable;
stdout?: Readable;
stderr?: Readable;
onExit?(
subprocess: Subprocess,
exitCode: number | null,
signalCode: number | null,
error?: ErrorLike,
): void | Promise<void>;
ipc?(message: any, subprocess: Subprocess): void;
serialization?: "json" | "advanced";
windowsHide?: boolean;
windowsVerbatimArguments?: boolean;
argv0?: string;
signal?: AbortSignal;
timeout?: number;
killSignal?: string | number;
maxBuffer?: number;
}
type Readable =
| "pipe"
| "inherit"
| "ignore"
| null // equivalent to "ignore"
| undefined // to use default
| BunFile
| ArrayBufferView
| number;
type Writable =
| "pipe"
| "inherit"
| "ignore"
| null // equivalent to "ignore"
| undefined // to use default
| BunFile
| ArrayBufferView
| number
| ReadableStream
| Blob
| Response
| Request;
}
interface Subprocess extends AsyncDisposable {
readonly stdin: FileSink | number | undefined;
readonly stdout: ReadableStream<Uint8Array> | number | undefined;
readonly stderr: ReadableStream<Uint8Array> | number | undefined;
readonly readable: ReadableStream<Uint8Array> | number | undefined;
readonly pid: number;
readonly exited: Promise<number>;
readonly exitCode: number | null;
readonly signalCode: NodeJS.Signals | null;
readonly killed: boolean;
kill(exitCode?: number | NodeJS.Signals): void;
ref(): void;
unref(): void;
send(message: any): void;
disconnect(): void;
resourceUsage(): ResourceUsage | undefined;
}
interface SyncSubprocess {
stdout: Buffer | undefined;
stderr: Buffer | undefined;
exitCode: number;
success: boolean;
resourceUsage: ResourceUsage;
signalCode?: string;
exitedDueToTimeout?: true;
pid: number;
}
interface ResourceUsage {
contextSwitches: {
voluntary: number;
involuntary: number;
};
cpuTime: {
user: number;
system: number;
total: number;
};
maxRSS: number;
messages: {
sent: number;
received: number;
};
ops: {
in: number;
out: number;
};
shmSize: number;
signalCount: number;
swapCount: number;
}
type Signal =
| "SIGABRT"
| "SIGALRM"
| "SIGBUS"
| "SIGCHLD"
| "SIGCONT"
| "SIGFPE"
| "SIGHUP"
| "SIGILL"
| "SIGINT"
| "SIGIO"
| "SIGIOT"
| "SIGKILL"
| "SIGPIPE"
| "SIGPOLL"
| "SIGPROF"
| "SIGPWR"
| "SIGQUIT"
| "SIGSEGV"
| "SIGSTKFLT"
| "SIGSTOP"
| "SIGSYS"
| "SIGTERM"
| "SIGTRAP"
| "SIGTSTP"
| "SIGTTIN"
| "SIGTTOU"
| "SIGUNUSED"
| "SIGURG"
| "SIGUSR1"
| "SIGUSR2"
| "SIGVTALRM"
| "SIGWINCH"
| "SIGXCPU"
| "SIGXFSZ"
| "SIGBREAK"
| "SIGLOST"
| "SIGINFO";