En node.js, se documenta que el flujo process.stdout se comporta "sincrónicamente", lo que, entre otras cosas, significa que cada llamada a stdout.write provoca una llamada al sistema de write inmediata, no hay almacenamiento en búfer. Por ejemplo
import { stdout } from 'process'; for (let i = 1; i <= 1000; i++) { stdout.write(`line ${i}\n`); } stdout.end(); hace 1000 llamadas al sistema de write . Esto no es lo que desea cuando está escribiendo una utilidad de emisión de datos tradicional de Unix. Es posible omitir process.stdout y crear una secuencia de escritura separada que se hunda en el descriptor de archivo 1, por ejemplo
import { stdout } from 'process'; import { createWriteStream } from 'fs'; let ostream = createWriteStream("/ignored", { fd: stdout.fd }); for (let i = 1; i <= 1000; i++) { ostream.write(`line ${i}\n`); } ostream.end(); hace una sola llamada al sistema. Sin embargo, los desvíos como este son peligrosos: después de la llamada ostream.end , el descriptor de archivo 1 se cierra pero el objeto process.stdout no lo sabe.
¿Hay alguna forma oficial de obtener una salida almacenada en búfer cuando se usa process.stdout ? Ideal en mi libro sería algo como el setvbuf de C.
También puede llamar a .end() en stdout. No estoy seguro de si esto lo hace más seguro.
import { promisify } from 'util'; import stream from 'stream'; import { stdout } from 'process'; import { createWriteStream } from 'fs'; const finished = promisify(stream.finished); let ostream = createWriteStream("/ignored", { fd: stdout.fd }); for (let i = 1; i <= 1000; i++) { ostream.write(`line ${i}\n`); } ostream.end(); await finished(ostream).then(()=> { console.log('Not quite done.'); stdout.end() console.log('Not gonna see this.'); });EDITAR: acabo de leer en los documentos de Node que en los sistemas POSIX, si usa tuberías para enviar datos a la salida estándar, es asíncrono. Así que el siguiente código debería funcionar:
import { stdout } from 'process'; import { Readable } from 'stream'; const data = new Readable() data._read = () => {}; data.pipe(stdout); for (let i = 1; i <= 1000; i++) { data.push(`line ${i}\n`); }process.stdout.cork()El método writable.cork() obliga a que todos los datos escritos se almacenen en la memoria. Los datos almacenados en el búfer se eliminarán cuando se llame a los métodos stream.uncork() o stream.end().
Nota: esto también console.log() .
process.stdout.cork(); for (let i = 1; i <= 1000; i++) { process.stdout.write(`line ${i}\n`); } process.stdout.uncork(); O, si desea vaciar el búfer cada vez que se alcanza el ciclo de eventos, puede anular write() :
const { stdout } = process; const { write } = stdout; stdout.write = function() { if (this.writableCorked == 0) { this.cork(); process.nextTick(() => this.uncork()); } write.apply(this, arguments); } for (let i = 1; i <= 1000; i++) { process.stdout.write(`line ${i}\n`); } Si le preocupa que el búfer se vuelva demasiado grande, también puede consultar stdout.writableNeedDrain .
strace -f -c -e trace=write,writev resultados:
% time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000006 1 4 write 0.00 0.000000 0 1 writev ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000006 5 totalProbado usando el nodo v16.13.2
Después de pensar un poco más en esto, la forma ideal de configurar el uso de process.stdout de manera segura y eficiente para la salida de datos masivos sería STDOUT_FILENO dup envolver un fs.WriteStream normal alrededor del duplicado y luego dup2 STDERR_FILENO sobre STDOUT_FILENO , lo que haría que todos los métodos del objeto de consola global y process.stdout escribieran en stderr en lugar de stdout. (Por lo que puedo decir, no hay otra forma de hacerlo). El código que genera los datos masivos luego escribiría en WriteStream normal en lugar de procesar.stdout.
O, en código:
import * as fs from "fs/promises"; import process from "process"; async function prepare_buffered_stdout(options) { options ??= {}; const stdout_copy = await fs.dup(process.stdout.fd); await fs.dup2(process.stderr.fd, process.stdout.fd); return fs.fdopen(stdout_copy, "w").createWriteStream(options); } Desafortunadamente, existe el problema menor de que las funciones fs.dup , fs.dup2 y fs.fdopen no existen. He presentado una solicitud de función para que se agreguen .