159 lines
5.8 KiB
Markdown
159 lines
5.8 KiB
Markdown
|
---
|
|||
|
title: Streams
|
|||
|
localeTitle: Corrientes
|
|||
|
---
|
|||
|
## Corrientes
|
|||
|
|
|||
|
Las secuencias están disponibles en la API central de Node.js como objetos que permiten que los datos se lean o escriban de forma continua. Básicamente, una secuencia hace eso en trozos en comparación con el búfer que hace su bit a bit, por lo que es un proceso lento.
|
|||
|
|
|||
|
Hay cuatro tipos de flujos disponibles:
|
|||
|
|
|||
|
* Legible (flujos desde los cuales se leen los datos)
|
|||
|
* Escritable (flujos en los que se escriben los datos)
|
|||
|
* Dúplex (secuencias que se pueden leer y escribir)
|
|||
|
* Transformar (transmisiones dúplex que pueden modificar datos a medida que se leen y escriben)
|
|||
|
|
|||
|
Cada tipo disponible tiene varios métodos asociados. Algunos de los más comunes son:
|
|||
|
|
|||
|
* datos (esto se ejecuta cuando los datos están disponibles)
|
|||
|
* final (esto se activa cuando no hay datos para leer)
|
|||
|
* error (esto se ejecuta cuando hay un error al recibir o escribir datos)
|
|||
|
|
|||
|
### Tubo
|
|||
|
|
|||
|
En programación, el concepto de `pipe` no es nuevo. Los sistemas basados en Unix han estado usándolo pragmáticamente desde la década de 1970. ¿Qué hace un tubo? Una `pipe` generalmente conecta la fuente y el destino. Pasa la salida de una función como la entrada de otra función.
|
|||
|
|
|||
|
En Node.js, la `pipe` se usa de la misma manera, para emparejar entradas y salidas de diferentes operaciones. `pipe()` está disponible como una función que toma un flujo de origen legible y adjunta la salida a un flujo de destino. La sintaxis general se puede representar como:
|
|||
|
|
|||
|
```javascript
|
|||
|
src.pipe(dest);
|
|||
|
```
|
|||
|
|
|||
|
Múltiples funciones `pipe()` también pueden ser encadenadas juntas.
|
|||
|
|
|||
|
```javascript
|
|||
|
a.pipe(b).pipe(c);
|
|||
|
|
|||
|
// which is equivalent to
|
|||
|
|
|||
|
a.pipe(b);
|
|||
|
b.pipe(c);
|
|||
|
```
|
|||
|
|
|||
|
### Corrientes legibles
|
|||
|
|
|||
|
Los flujos que producen datos que se pueden adjuntar como entrada a un flujo grabable se conocen como flujo legible. Para crear un flujo legible:
|
|||
|
|
|||
|
```javascript
|
|||
|
const { Readable } = require('stream');
|
|||
|
|
|||
|
const readable = new Readable();
|
|||
|
|
|||
|
readable.on('data', chunk => {
|
|||
|
console.log(`Received ${chunk.length} bytes of data.`);
|
|||
|
});
|
|||
|
readable.on('end', () => {
|
|||
|
console.log('There will be no more data.');
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
### Secuencia de escritura
|
|||
|
|
|||
|
Este es el tipo de flujo al que puede `pipe()` los datos desde una fuente legible. Para crear un flujo de escritura, tenemos un enfoque de constructor. Creamos un objeto a partir de él y pasamos una serie de opciones. El método toma tres argumentos:
|
|||
|
|
|||
|
* chunk: un buffer
|
|||
|
* codificación: para convertir datos a un formato legible por humanos
|
|||
|
* devolución de llamada: una función que se llama cuando los datos se procesan desde el fragmento
|
|||
|
|
|||
|
```javascript
|
|||
|
const { Writable } = require('stream');
|
|||
|
const writable = new Writable({
|
|||
|
write(chunk, encoding, callback) {
|
|||
|
console.log(chunk.toString());
|
|||
|
callback();
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
process.stdin.pipe(writable);
|
|||
|
```
|
|||
|
|
|||
|
### Streams Duplex
|
|||
|
|
|||
|
Los flujos dúplex nos ayudan a implementar flujos legibles y grabables al mismo tiempo.
|
|||
|
|
|||
|
```javascript
|
|||
|
const { Duplex } = require('stream');
|
|||
|
|
|||
|
const inoutStream = new Duplex({
|
|||
|
write(chunk, encoding, callback) {
|
|||
|
console.log(chunk.toString());
|
|||
|
callback();
|
|||
|
},
|
|||
|
|
|||
|
read(size) {
|
|||
|
this.push(String.fromCharCode(this.currentCharCode++));
|
|||
|
if (this.currentCharCode > 90) {
|
|||
|
this.push(null);
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
inoutStream.currentCharCode = 65;
|
|||
|
process.stdin.pipe(inoutStream).pipe(process.stdout);
|
|||
|
```
|
|||
|
|
|||
|
El flujo `stdin` canaliza los datos legibles en el flujo dúplex. El `stdout` nos ayuda a ver los datos. Las partes legibles y grabables de un flujo dúplex operan completamente independientes entre sí.
|
|||
|
|
|||
|
### Corriente de transformación
|
|||
|
|
|||
|
Este tipo de flujo es más una versión avanzada del flujo dúplex.
|
|||
|
|
|||
|
```javascript
|
|||
|
const { Transform } = require('stream');
|
|||
|
|
|||
|
const upperCaseTr = new Transform({
|
|||
|
transform(chunk, encoding, callback) {
|
|||
|
this.push(chunk.toString().toUpperCase());
|
|||
|
callback();
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
process.stdin.pipe(upperCaseTr).pipe(process.stdout);
|
|||
|
```
|
|||
|
|
|||
|
Los datos que estamos consumiendo son los mismos que en el ejemplo anterior de la transmisión dúplex. Lo que hay que notar aquí es que `transform()` no requiere la implementación de métodos de `read` o `write` . Combina ambos métodos en sí.
|
|||
|
|
|||
|
### ¿Por qué usar Streams?
|
|||
|
|
|||
|
Dado que Node.js es asíncrono, interactúa pasando devoluciones de llamada a funciones con disco y red. Un ejemplo dado a continuación lee los datos de un archivo en el disco y los responde a través de la solicitud de red del cliente.
|
|||
|
|
|||
|
```javascript
|
|||
|
const http = require('http');
|
|||
|
const fs = require('fs');
|
|||
|
|
|||
|
const server = http.createServer((req, res) => {
|
|||
|
fs.readFile('data.txt', (err, data) => {
|
|||
|
res.end(data);
|
|||
|
});
|
|||
|
});
|
|||
|
server.listen(8000);
|
|||
|
```
|
|||
|
|
|||
|
El fragmento de código anterior funcionará, pero todos los datos del archivo irán primero a la memoria para cada solicitud antes de volver a escribir el resultado en la solicitud del cliente. Si el archivo que estamos leyendo es demasiado grande, esto puede convertirse en una llamada de servidor muy pesada y costosa, ya que consumirá una gran cantidad de memoria para que el proceso avance. La experiencia del usuario en el lado del cliente también sufrirá demora.
|
|||
|
|
|||
|
En este caso, si utilizamos secuencias, los datos se enviarán a la solicitud del cliente como una parte a la vez, tan pronto como se reciban del disco.
|
|||
|
|
|||
|
```javascript
|
|||
|
const http = require('http');
|
|||
|
const fs = require('fs');
|
|||
|
|
|||
|
const server = http.createServer((req, res) => {
|
|||
|
const stream = fs.createReadStream('data.txt');
|
|||
|
stream.pipe(res);
|
|||
|
});
|
|||
|
server.listen(8000);
|
|||
|
```
|
|||
|
|
|||
|
El `pipe()` aquí se ocupa de la escritura o, en nuestro caso, el envío de los datos con el objeto de respuesta y una vez que se leen todos los datos del archivo, para cerrar la conexión.
|
|||
|
|
|||
|
Nota: `process.stdin` y `process.stdout` se crean en secuencias en el objeto de `process` global proporcionado por la API Node.js.
|