¿Cómo cerrar una secuencia legible (antes del final)?

90

¿Cómo cerrar una secuencia legible en Node.js?

var input = fs.createReadStream('lines.txt');

input.on('data', function(data) {
   // after closing the stream, this will not
   // be called again

   if (gotFirstLine) {
      // close this stream and continue the
      // instructions from this if
      console.log("Closed.");
   }
});

Esto sería mejor que:

input.on('data', function(data) {
   if (isEnded) { return; }

   if (gotFirstLine) {
      isEnded = true;
      console.log("Closed.");
   }
});

Pero esto no detendría el proceso de lectura ...

Ionică Bizău
fuente
6
Advertencia: esta pregunta es solo en el contexto del fsmódulo. closeno existe en Stream.Readable.
Zamnuts
3
Buenas noticias. La versión 8 del nodo proporcionastream.destroy()
joeytwiddle
no puedes llamarreadable.push(null) && readable.destroy();
Alexander Mills

Respuestas:

39

Invocar input.close(). No está en los documentos, pero

https://github.com/joyent/node/blob/cfcb1de130867197cbc9c6012b7e84e08e53d032/lib/fs.js#L1597-L1620

claramente hace el trabajo :) En realidad hace algo similar a tu isEnded.

EDITAR 2015-Apr-19 Basado en los comentarios a continuación, y para aclarar y actualizar:

  • Esta sugerencia es un truco y no está documentada.
  • Aunque para mirar la corriente lib/fs.jstodavía funciona> 1,5 años después.
  • Estoy de acuerdo con el comentario a continuación acerca de que destroy()es preferible llamar .
  • Como se indica correctamente a continuación, esto funciona para fs ReadStreams's, no en un genéricoReadable

En cuanto a una solución genérica: no parece que haya una, al menos según mi comprensión de la documentación y de un vistazo rápido a _stream_readable.js.

Mi propuesta sería poner su flujo legible en modo pausado , al menos evitando un procesamiento adicional en su fuente de datos ascendente. No olvide unpipe()eliminar todos los datadetectores de eventos para que se pause()pause, como se menciona en los documentos.

Nitzan sacudido
fuente
Hazlo más largo: añade algunas referencias de la documentación. :-)
Ionică Bizău
2
¡Muy bien! El código fuente parece ser el mejor recurso de documentación. ;-)
Ionică Bizău
En realidad, preferiría llamar en su destroylugar. Al menos eso es lo que se llama si establece autoClose en verdadero. Al mirar el código fuente (hoy) las diferencias son mínimas ( destroyllamadas close) pero eso podría cambiar en el futuro
Marcelo Diniz
@NitzanShaked El archivo fue actualizado. ¿Es este el enlace correcto: github.com/joyent/node/blob/… ? (contiene el ID de confirmación, por lo que no se cambiará en el futuro)
Ionică Bizău
4
No hay close()un objeto legible, ¿nunca hay una solución? Mi intercambio de datos siempre está incompleto ...
CodeManX
74

Editar: ¡ Buenas noticias! Comenzar con Node.js 8.0.0 readable.destroyestá oficialmente disponible: https://nodejs.org/api/stream.html#stream_readable_destroy_error

ReadStream.destroy

Puede llamar a la función ReadStream.destroy en cualquier momento.

var fs = require('fs');

var readStream = fs.createReadStream('lines.txt');
readStream
    .on('data', function (chunk) {
        console.log(chunk);
        readStream.destroy();
    })
    .on('end', function () {
        // This may not been called since we are destroying the stream
        // the first time 'data' event is received
        console.log('All the data in the file has been read');
    })
    .on('close', function (err) {
        console.log('Stream has been destroyed and file has been closed');
    });

La función pública ReadStream.destroyno está documentada (Node.js v0.12.2) pero puedes echar un vistazo al código fuente en GitHub ( confirmación del 5 de octubre de 2012 ).

La destroyfunción marca internamente la ReadStreaminstancia como destruida y llama a la closefunción para liberar el archivo.

Puede escuchar el evento de cierre para saber exactamente cuándo se cierra el archivo. El evento final no se activará a menos que los datos se consuman por completo.


Tenga en cuenta que las destroy(y close) funciones son específicas de fs.ReadStream . No forman parte de la "interfaz" legible de flujo genérico .

Yves M.
fuente
Al menos en la última versión de Node (no he comprobado las demás), el descriptor de archivo se cierra automáticamente . Dicho esto, no he realizado ningún tipo de prueba exhaustiva para asegurarme de que la transmisión finalmente se active errorsi nunca se lee. Aparte de eso, la única otra filtración de la que me preocuparía son los controladores de eventos; una vez más, no estoy 100% seguro de esto, pero podríamos estar bien porque el 2010 el evangelio de Isaacs dice que los controladores son podado cuando los emisores son gc'd: groups.google.com/d/msg/nodejs/pXbJVo0NtaY/BxUmF_jp9LkJ
mikermcneil
1
Si los datos son demasiado pequeños, on('data') solo se activará una vez, por lo que no habrá ninguno .close(), solo recuerde a alguien más.
bitfishxyz
12

No puedes. No hay manera de cerrar / apagado / aborto / destruir un genérico sin documentados legible corriente como del Nodo 5.3.0. Ésta es una limitación de la arquitectura del flujo de nodo.

Como han explicado otras respuestas aquí, existen hacks no documentados para implementaciones específicas de Readable proporcionadas por Node, como fs.ReadStream . Sin embargo, estas no son soluciones genéricas para cualquier legible.

Si alguien puede demostrar que estoy equivocado, por favor hágalo. Me gustaría poder hacer lo que digo que es imposible, y estaría encantado de que me corrigieran.

EDITAR : Aquí estaba mi solución: implementar .destroy()para mi canalización a través de una serie compleja de unpipe()llamadas. Y después de toda esa complejidad, no funciona correctamente en todos los casos .

EDITAR : El nodo v8.0.0 agregó una destroy()api para transmisiones legibles .

thejoshwolfe
fuente
1
Hay ahora stream.pipeline, que afirma manejar "errores de reenvío y limpieza adecuada y proporcionar una devolución de llamada cuando se completa la canalización". ¿Eso ayuda?
andrewdotn
11

En la versión, la 4.*.*inserción de un valor nulo en la secuencia activará una EOFseñal.

De los documentos de nodejs

Si se pasa un valor que no sea nulo, el método push () agrega una parte de los datos a la cola para que los procesadores de flujo posteriores consuman. Si se pasa nulo, indica el final de la secuencia (EOF), después de lo cual no se pueden escribir más datos.

Esto funcionó para mí después de probar muchas otras opciones en esta página.

Jacob Lowe
fuente
1
Funciona para mi. Sin embargo, necesitaba evitar llamar a la devolución de llamada done () después de presionar nulo para obtener el comportamiento esperado, es decir, que toda la transmisión se detenga.
Rich Apodaca
6

Este módulo de destrucción está destinado a garantizar que una secuencia se destruya, manejando diferentes API y errores de Node.js. Ahora mismo es una de las mejores opciones.

NÓTESE BIEN. Desde el Nodo 10 puede utilizar el .destroymétodo sin más dependencias.

Rocco Musolino
fuente
3

Puede limpiar y cerrar el flujo con yourstream.resume(), lo que volcará todo en el flujo y finalmente lo cerrará.

De los documentos oficiales :

readable.resume ():

Regreso: este

Este método hará que la secuencia legible reanude la emisión de eventos de 'datos'.

Este método cambiará la corriente a modo de flujo. Si no desea consumir los datos de una secuencia, pero desea llegar a su evento 'final', puede llamar a stream.resume () para abrir el flujo de datos.

var readable = getReadableStreamSomehow();
readable.resume();
readable.on('end', () => {
  console.log('got to the end, but did not read anything');
});
Sayem
fuente
A esto se le puede llamar "drenar" la corriente. En nuestro caso, por supuesto, teníamos un 'data'detector de eventos, pero lo hicimos verificar un booleano if (!ignoring) { ... }para que no procese datos cuando estamos agotando la transmisión. ignoring = true; readable.resume();
joeytwiddle
5
Por supuesto, esto supone que la transmisión lo hará 'end'en algún momento. ¡No todas las transmisiones harán eso! (Por ejemplo, una transmisión que envía la fecha cada segundo, para siempre.)
joeytwiddle
3

Es una pregunta antigua, pero yo también estaba buscando la respuesta y encontré la mejor para mi implementación. Tanto endy closeeventos consiguen emiten así que creo que esta es la solución más limpia.

Esto hará el truco en el nodo 4.4. * (Versión estable en el momento de escribir este artículo):

var input = fs.createReadStream('lines.txt');

input.on('data', function(data) {
   if (gotFirstLine) {
      this.end(); // Simple isn't it?
      console.log("Closed.");
   }
});

Para obtener una explicación muy detallada, consulte: http://www.bennadel.com/blog/2692-you-have-to-explicitly-end-streams-after-pipes-break-in-node-js.htm

Vexter
fuente
2

Este código aquí hará el truco muy bien:

function closeReadStream(stream) {
    if (!stream) return;
    if (stream.close) stream.close();
    else if (stream.destroy) stream.destroy();
}

writeStream.end () es la forma de ir para cerrar un writeStream ...

g00dnatur3
fuente
1
¿Por qué mencionas que .end () es la forma de ir, pero luego tu código usa cerrar y destruir y ni siquiera usa final?
Lucas B
1
estoy cerrando un readStream en el ejemplo ... un writeStream - use.end
g00dnatur3