Ejecutar un proceso (Bun.spawn())
Proporciona un comando como un array de cadenas. El resultado de Bun.spawn() es un objeto Bun.Subprocess.
const proc = Bun.spawn(["bun", "--version"]);
console.log(await proc.exited); // 0El segundo argumento de Bun.spawn es un objeto de parámetros que se puede usar para configurar el subproceso.
const proc = Bun.spawn(["bun", "--version"], {
cwd: "./path/to/subdir", // especifica un directorio de trabajo
env: { ...process.env, FOO: "bar" }, // especifica variables de entorno
onExit(proc, exitCode, signalCode, error) {
// manejador de salida
},
});
proc.pid; // ID de proceso del subprocesoStream de entrada
Por defecto, el stream de entrada del subproceso no está definido; se puede configurar con el parámetro 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); ..."| Valor | Descripción |
|---|---|
null | Predeterminado. No proporciona entrada al subproceso |
"pipe" | Devuelve un FileSink para escritura incremental rápida |
"inherit" | Hereda el stdin del proceso padre |
Bun.file() | Lee desde el archivo especificado |
TypedArray | DataView | Usa un buffer binario como entrada |
Response | Usa el body de la respuesta como entrada |
Request | Usa el body de la solicitud como entrada |
ReadableStream | Usa un stream legible como entrada |
Blob | Usa un blob como entrada |
number | Lee desde el archivo con un descriptor de archivo dado |
La opción "pipe" te permite escribir incrementalmente en el stream de entrada del subproceso desde el proceso padre.
const proc = Bun.spawn(["cat"], {
stdin: "pipe", // devuelve un FileSink para escribir
});
// encola datos de cadena
proc.stdin.write("hello");
// encola datos binarios
const enc = new TextEncoder();
proc.stdin.write(enc.encode(" world!"));
// envía datos en buffer
proc.stdin.flush();
// cierra el stream de entrada
proc.stdin.end();Pasar un ReadableStream a stdin te permite canalizar datos desde un ReadableStream de JavaScript directamente a la entrada del subproceso:
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!"Streams de salida
Puedes leer resultados del subproceso mediante las propiedades stdout y stderr. Por defecto, estas son instancias de ReadableStream.
const proc = Bun.spawn(["bun", "--version"]);
const text = await proc.stdout.text();
console.log(text); // => "1.3.3\n"Configura el stream de salida pasando uno de los siguientes valores a stdout/stderr:
| Valor | Descripción |
|---|---|
"pipe" | Predeterminado para stdout. Canaliza la salida a un ReadableStream en el objeto Subprocess devuelto |
"inherit" | Predeterminado para stderr. Hereda del proceso padre |
"ignore" | Descarta la salida |
Bun.file() | Escribe en el archivo especificado |
number | Escribe en el archivo con el descriptor de archivo dado |
Manejo de salida
Usa el callback onExit para escuchar cuando el proceso sale o es terminado.
const proc = Bun.spawn(["bun", "--version"], {
onExit(proc, exitCode, signalCode, error) {
// manejador de salida
},
});Por conveniencia, la propiedad exited es una Promise que se resuelve cuando el proceso sale.
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited; // se resuelve cuando el proceso sale
proc.killed; // booleano — ¿fue terminado el proceso?
proc.exitCode; // null | number
proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...Para terminar un proceso:
const proc = Bun.spawn(["bun", "--version"]);
proc.kill();
proc.killed; // true
proc.kill(15); // especifica un código de señal
proc.kill("SIGTERM"); // especifica un nombre de señalEl proceso padre bun no terminará hasta que todos los procesos hijos hayan salido. Usa proc.unref() para desvincular el proceso hijo del padre.
const proc = Bun.spawn(["bun", "--version"]);
proc.unref();Uso de recursos
Puedes obtener información sobre el uso de recursos del proceso después de que haya salido:
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited;
const usage = proc.resourceUsage();
console.log(`Memoria máxima usada: ${usage.maxRSS} bytes`);
console.log(`Tiempo de CPU (usuario): ${usage.cpuTime.user} µs`);
console.log(`Tiempo de CPU (sistema): ${usage.cpuTime.system} µs`);Usar AbortSignal
Puedes abortar un subproceso usando un AbortSignal:
const controller = new AbortController();
const { signal } = controller;
const proc = Bun.spawn({
cmd: ["sleep", "100"],
signal,
});
// Más tarde, para abortar el proceso:
controller.abort();Usar timeout y killSignal
Puedes establecer un tiempo de espera para que un subproceso termine automáticamente después de una duración específica:
# Termina el proceso después de 5 segundos
const proc = Bun.spawn({
cmd: ["sleep", "10"],
timeout: 5000, // 5 segundos en milisegundos
});
await proc.exited; // Se resolverá después de 5 segundosPor defecto, los procesos que exceden el tiempo de espera se terminan con la señal SIGTERM. Puedes especificar una señal diferente con la opción killSignal:
# Termina el proceso con SIGKILL después de 5 segundos
const proc = Bun.spawn({
cmd: ["sleep", "10"],
timeout: 5000,
killSignal: "SIGKILL", // Puede ser nombre de señal o número de señal
});La opción killSignal también controla qué señal se envía cuando un AbortSignal es abortado.
Usar maxBuffer
Para spawnSync, puedes limitar el número máximo de bytes de salida antes de que el proceso sea terminado:
# Termina 'yes' después de que emita más de 100 bytes de salida
const result = Bun.spawnSync({
cmd: ["yes"], // o ["bun", "exec", "yes"] en Windows
maxBuffer: 100,
});
// el proceso saleComunicación entre procesos (IPC)
Bun soporta un canal de comunicación entre procesos directo entre dos procesos bun. Para recibir mensajes de un subproceso Bun ejecutado, especifica un manejador ipc.
const child = Bun.spawn(["bun", "child.ts"], {
ipc(message) {
/**
* El mensaje recibido del subproceso
**/
},
});El proceso padre puede enviar mensajes al subproceso usando el método .send() en la instancia Subprocess devuelta. Una referencia al subproceso que envía también está disponible como el segundo argumento en el manejador ipc.
const childProc = Bun.spawn(["bun", "child.ts"], {
ipc(message, childProc) {
/**
* El mensaje recibido del subproceso
**/
childProc.send("Responder al hijo");
},
});
childProc.send("Soy tu padre"); // El padre también puede enviar mensajes al hijoMientras tanto, el proceso hijo puede enviar mensajes a su padre usando process.send() y recibir mensajes con process.on("message"). Esta es la misma API usada para child_process.fork() en Node.js.
process.send("Hola desde hijo como cadena");
process.send({ message: "Hola desde hijo como objeto" });
process.on("message", message => {
// imprimir mensaje del padre
console.log(message);
});// enviar una cadena
process.send("Hola desde hijo como cadena");
// enviar un objeto
process.send({ message: "Hola desde hijo como objeto" });La opción serialization controla el formato de comunicación subyacente entre los dos procesos:
advanced: (predeterminado) Los mensajes se serializan usando la APIserializede JSC, que soporta clonar todo lo que soportastructuredClone. Esto no soporta transferir propiedad de objetos.json: Los mensajes se serializan usandoJSON.stringifyyJSON.parse, lo cual no soporta tantos tipos de objetos comoadvanced.
Para desconectar el canal IPC desde el proceso padre, llama:
childProc.disconnect();IPC entre Bun y Node.js
Para usar IPC entre un proceso bun y un proceso Node.js, establece serialization: "json" en Bun.spawn. Esto es porque Node.js y Bun usan diferentes motores de JavaScript con diferentes formatos de serialización de objetos.
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} 👋 hola node` });
node.kill();
},
stdio: ["inherit", "inherit", "inherit"],
serialization: "json",
});
node.send({ message: `${prefix} 👋 hola node` });
} else {
const prefix = `[node ${process.version}]`;
process.on("message", ({ message }) => {
console.log(message);
process.send({ message: `${prefix} 👋 hola bun` });
});
}API de Bloqueo (Bun.spawnSync())
Bun proporciona un equivalente síncrono de Bun.spawn llamado Bun.spawnSync. Esta es una API de bloqueo que soporta las mismas entradas y parámetros que Bun.spawn. Devuelve un objeto SyncSubprocess, que difiere de Subprocess en algunas formas.
- Contiene una propiedad
successque indica si el proceso salió con un código de salida cero. - Las propiedades
stdoutystderrson instancias deBufferen lugar deReadableStream. - No hay una propiedad
stdin. UsaBun.spawnpara escribir incrementalmente en el stream de entrada del subproceso.
const proc = Bun.spawnSync(["echo", "hello"]);
console.log(proc.stdout.toString());
// => "hello\n"Como regla general, la API asíncrona Bun.spawn es mejor para servidores HTTP y aplicaciones, y Bun.spawnSync es mejor para construir herramientas de línea de comandos.
Benchmarks
NOTE
⚡️ Bajo el capó, `Bun.spawn` y `Bun.spawnSync` usan [`posix_spawn(3)`](https://man7.org/linux/man-pages/man3/posix_spawn.3.html).El spawnSync de Bun ejecuta procesos un 60% más rápido que el módulo child_process de 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 msReferencia
A continuación se muestra una referencia de la API Spawn y los tipos. Los tipos reales tienen genéricos complejos para tipar fuertemente los streams Subprocess con las opciones pasadas a Bun.spawn y Bun.spawnSync. Para detalles completos, encuentra estos tipos como se definen en 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 // equivalente a "ignore"
| undefined // para usar predeterminado
| BunFile
| ArrayBufferView
| number;
type Writable =
| "pipe"
| "inherit"
| "ignore"
| null // equivalente a "ignore"
| undefined // para usar predeterminado
| 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";