Eseguire un processo (Bun.spawn())
Fornisci un comando come array di stringhe. Il risultato di Bun.spawn() è un oggetto Bun.Subprocess.
const proc = Bun.spawn(["bun", "--version"]);
console.log(await proc.exited); // 0Il secondo argomento di Bun.spawn è un oggetto di parametri che può essere usato per configurare il subprocesso.
const proc = Bun.spawn(["bun", "--version"], {
cwd: "./path/to/subdir", // specifica una directory di lavoro
env: { ...process.env, FOO: "bar" }, // specifica variabili d'ambiente
onExit(proc, exitCode, signalCode, error) {
// handler di uscita
},
});
proc.pid; // ID del processo del subprocessoStream di input
Di default, lo stream di input del subprocesso è undefined; può essere configurato con il parametro 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); ..."| Valore | Descrizione |
|---|---|
null | Default. Non fornire input al subprocesso |
"pipe" | Restituisce un FileSink per scrittura incrementale veloce |
"inherit" | Eredita lo stdin del processo genitore |
Bun.file() | Legge dal file specificato |
TypedArray | DataView | Usa un buffer binario come input |
Response | Usa il body della response come input |
Request | Usa il body della request come input |
ReadableStream | Usa uno stream leggibile come input |
Blob | Usa un blob come input |
number | Legge dal file con un dato file descriptor |
L'opzione "pipe" permette di scrivere incrementalmente nello stream di input del subprocesso dal processo genitore.
const proc = Bun.spawn(["cat"], {
stdin: "pipe", // restituisce un FileSink per scrivere
});
// accoda dati stringa
proc.stdin.write("hello");
// accoda dati binari
const enc = new TextEncoder();
proc.stdin.write(enc.encode(" world!"));
// invia dati bufferizzati
proc.stdin.flush();
// chiude lo stream di input
proc.stdin.end();Passare un ReadableStream a stdin permette di inviare dati da un ReadableStream JavaScript direttamente all'input del subprocesso:
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!"Stream di output
Puoi leggere i risultati dal subprocesso tramite le proprietà stdout e stderr. Di default queste sono istanze di ReadableStream.
const proc = Bun.spawn(["bun", "--version"]);
const text = await proc.stdout.text();
console.log(text); // => "1.3.3\n"Configura lo stream di output passando uno dei seguenti valori a stdout/stderr:
| Valore | Descrizione |
|---|---|
"pipe" | Default per stdout. Invia l'output a un ReadableStream sull'oggetto Subprocess restituito |
"inherit" | Default per stderr. Eredita dal processo genitore |
"ignore" | Scarta l'output |
Bun.file() | Scrive sul file specificato |
number | Scrive sul file con il dato file descriptor |
Gestione dell'uscita
Usa il callback onExit per ascoltare quando il processo esce o viene terminato.
const proc = Bun.spawn(["bun", "--version"], {
onExit(proc, exitCode, signalCode, error) {
// handler di uscita
},
});Per comodità, la proprietà exited è una Promise che si risolve quando il processo esce.
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited; // si risolve quando il processo esce
proc.killed; // boolean — il processo è stato terminato?
proc.exitCode; // null | number
proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...Per terminare un processo:
const proc = Bun.spawn(["bun", "--version"]);
proc.kill();
proc.killed; // true
proc.kill(15); // specifica un codice di segnale
proc.kill("SIGTERM"); // specifica un nome di segnaleIl processo genitore bun non terminerà finché tutti i processi figlio non sono usciti. Usa proc.unref() per scollegare il processo figlio dal genitore.
const proc = Bun.spawn(["bun", "--version"]);
proc.unref();Utilizzo delle risorse
Puoi ottenere informazioni sull'utilizzo delle risorse del processo dopo che è uscito:
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited;
const usage = proc.resourceUsage();
console.log(`Memoria massima usata: ${usage.maxRSS} byte`);
console.log(`Tempo CPU (user): ${usage.cpuTime.user} µs`);
console.log(`Tempo CPU (system): ${usage.cpuTime.system} µs`);Usare AbortSignal
Puoi interrompere un subprocesso usando un AbortSignal:
const controller = new AbortController();
const { signal } = controller;
const proc = Bun.spawn({
cmd: ["sleep", "100"],
signal,
});
// Più tardi, per interrompere il processo:
controller.abort();Usare timeout e killSignal
Puoi impostare un timeout per un subprocesso per terminare automaticamente dopo una durata specifica:
// Termina il processo dopo 5 secondi
const proc = Bun.spawn({
cmd: ["sleep", "10"],
timeout: 5000, // 5 secondi in millisecondi
});
await proc.exited; // Si risolve dopo 5 secondiDi default, i processi con timeout vengono terminati con il segnale SIGTERM. Puoi specificare un segnale diverso con l'opzione killSignal:
// Termina il processo con SIGKILL dopo 5 secondi
const proc = Bun.spawn({
cmd: ["sleep", "10"],
timeout: 5000,
killSignal: "SIGKILL", // Può essere nome stringa o numero di segnale
});L'opzione killSignal controlla anche quale segnale viene inviato quando un AbortSignal viene interrotto.
Usare maxBuffer
Per spawnSync, puoi limitare il numero massimo di byte di output prima che il processo venga terminato:
// Termina 'yes' dopo che emette oltre 100 byte di output
const result = Bun.spawnSync({
cmd: ["yes"], // o ["bun", "exec", "yes"] su Windows
maxBuffer: 100,
});
// il processo esceComunicazione inter-processo (IPC)
Bun supporta un canale di comunicazione inter-processo diretto tra due processi bun. Per ricevere messaggi da un subprocesso Bun spawnato, specifica un handler ipc.
const child = Bun.spawn(["bun", "child.ts"], {
ipc(message) {
/**
* Il messaggio ricevuto dal subprocesso
**/
},
});Il processo genitore può inviare messaggi al subprocesso usando il metodo .send() sull'istanza Subprocess restituita. Un riferimento al subprocesso mittente è anche disponibile come secondo argomento nell'handler ipc.
const childProc = Bun.spawn(["bun", "child.ts"], {
ipc(message, childProc) {
/**
* Il messaggio ricevuto dal subprocesso
**/
childProc.send("Rispondi al figlio");
},
});
childProc.send("Sono tuo padre"); // Il genitore può anche inviare messaggi al figlioNel frattempo il processo figlio può inviare messaggi al suo genitore usando process.send() e ricevere messaggi con process.on("message"). Questa è la stessa API usata per child_process.fork() in Node.js.
process.send("Ciao dal figlio come stringa");
process.send({ message: "Ciao dal figlio come oggetto" });
process.on("message", message => {
// stampa messaggio dal genitore
console.log(message);
});// invia una stringa
process.send("Ciao dal figlio come stringa");
// invia un oggetto
process.send({ message: "Ciao dal figlio come oggetto" });L'opzione serialization controlla il formato di comunicazione sottostante tra i due processi:
advanced: (default) I messaggi vengono serializzati usando l'APIserializedi JSC, che supporta la clonazione di tutto ciò chestructuredClonesupporta. Questo non supporta il trasferimento di proprietà di oggetti.json: I messaggi vengono serializzati usandoJSON.stringifyeJSON.parse, che non supporta tanti tipi di oggetti quantoadvanced.
Per disconnettere il canale IPC dal processo genitore, chiama:
childProc.disconnect();IPC tra Bun e Node.js
Per usare IPC tra un processo bun e un processo Node.js, imposta serialization: "json" in Bun.spawn. Questo perché Node.js e Bun usano motori JavaScript diversi con formati di serializzazione degli oggetti diversi.
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} 👋 ciao node` });
node.kill();
},
stdio: ["inherit", "inherit", "inherit"],
serialization: "json",
});
node.send({ message: `${prefix} 👋 ciao node` });
} else {
const prefix = `[node ${process.version}]`;
process.on("message", ({ message }) => {
console.log(message);
process.send({ message: `${prefix} 👋 ciao bun` });
});
}API Bloccante (Bun.spawnSync())
Bun fornisce un equivalente sincrono di Bun.spawn chiamato Bun.spawnSync. Questa è un'API bloccante che supporta gli stessi input e parametri di Bun.spawn. Restituisce un oggetto SyncSubprocess, che differisce da Subprocess in alcuni modi.
- Contiene una proprietà
successche indica se il processo è uscito con codice di uscita zero. - Le proprietà
stdoutestderrsono istanze diBufferinvece diReadableStream. - Non c'è proprietà
stdin. UsaBun.spawnper scrivere incrementalmente nello stream di input del subprocesso.
const proc = Bun.spawnSync(["echo", "hello"]);
console.log(proc.stdout.toString());
// => "hello\n"Come regola generale, l'API asincrona Bun.spawn è migliore per server HTTP e app, e Bun.spawnSync è migliore per costruire strumenti da riga di comando.
Benchmark
NOTE
⚡️ Sotto il cofano, `Bun.spawn` e `Bun.spawnSync` usano [`posix_spawn(3)`](https://man7.org/linux/man-pages/man3/posix_spawn.3.html).Lo spawnSync di Bun esegue processi il 60% più velocemente del modulo child_process di 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 msRiferimento
Un riferimento dell'API Spawn e dei tipi è mostrato sotto. I tipi reali hanno generici complessi per tipizzare fortemente gli stream Subprocess con le opzioni passate a Bun.spawn e Bun.spawnSync. Per i dettagli completi, trova questi tipi come definiti in 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 // per usare il default
| BunFile
| ArrayBufferView
| number;
type Writable =
| "pipe"
| "inherit"
| "ignore"
| null // equivalente a "ignore"
| undefined // per usare il 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";