Cómo usar temporizadores y eventos en Node.js

C

Eventos y temporizadores en Node.js

Node.js tiene múltiples utilidades para manejar eventos así como para programar la ejecución de código. Estas utilidades, combinadas, le brindan la capacidad de responder de manera reactiva en el momento adecuado, por ejemplo:

  • Borrar los datos de la sesión cuando un usuario cierra la sesión
  • Programar un tiempo de espera para recibir resultados de una llamada a la API y especificar el código de manejo de errores para ejecutar en caso de tiempo de espera
  • Cerrar las conexiones de la base de datos antes de salir de Node.js

En este artículo, repasaremos cómo temporizadores trabajar en Node.js. También presentaremos cómo funciona el bucle de eventos de Node.js y cómo puede aprovechar las capacidades de manejo de eventos de Node.

Temporizadores

El primer conjunto de utilidades que veremos son las setTimeout, setImmediatey setInterval Utilidades de cronometraje. Con estas herramientas, podemos controlar el tiempo de ejecución del código.

¿Por qué esto sería importante? En Node.js, similar a cuando se usan otros lenguajes de programación como C, Python, Java y otros, ayuda programar ciertas funciones para que se ejecuten repetidamente.

Supongamos, por ejemplo, que queremos copiar ciertos archivos de una ubicación de recepción a un archivo permanente. Este sería un buen escenario para programar una transferencia de archivos. A determinados intervalos, podemos comprobar si hay nuevos archivos y luego copiarlos en la ubicación de la copia de seguridad, si hay alguno.

setTimeout

Con setTimeout, podemos programar el código para que se ejecute después de que haya pasado un cierto tiempo.

// setTimeout.js

let cue="The actors are here!";

// However, the cue is not announced until at least 5000ms have
// passed through the use of setTimeout
setTimeout(function() {
    return console.log(cue);
}, 5000);

// This console log is executed right away
console.log('An exploration of art and music. And now, as we wait for the actors...');

Para ejecutar este código y verlo en acción, ejecute node setTimeout.js en tu terminal:

$ node setTimeout.js
An exploration of art and music. And now, as we wait for the actors...
The actors are here!

Observe cómo aunque el console('An exploration...') la llamada es después de nuestra console.log(cue) call, todavía se ejecuta primero.

El truco para darse cuenta aquí es que el código solo se garantiza que se ejecute después de que haya pasado al menos ese período de tiempo, no justo en el punto.

setInterval

En situaciones en las que necesite una ejecución de código repetida y regular, como sondeos largos, setInterval El método será un ajuste más natural que setTimeout. Con esta función, podemos especificar una función que se ejecutará cada X segundos. La función en realidad toma su argumento en milisegundos, por lo que debe hacer la conversión usted mismo antes de ingresar sus argumentos.

Supongamos que queremos verificar la longitud de la cola en un McDonald’s drive-through para que los usuarios de nuestro programa puedan salir corriendo en el mejor momento. Utilizando setInterval, podemos comprobar repetidamente la longitud de la cola y decirles cuando la costa está despejada.

// setInterval.js

// This function simulates us checking the length
// of a McDonald's drive-through queue
let getQueueLength = function() {
    return Math.round(12 * Math.random());
};

// We would like to retrieve the queue length at regular intervals
// this way, we can decide when to make a quick dash over
// at the optimal time
setInterval(function() {
    let queueLength = getQueueLength();

    console.log(`The queue at the McDonald's drive-through is now ${queueLength} cars long.`);

    if (queueLength === 0) {
        console.log('Quick, grab your coat!');
    }

    if (queueLength > 8) {
        return console.log('This is beginning to look impossible!');
    }
}, 3000);

Puede ver el resultado a continuación. Ejecute el código con node setInterval.js, Como se muestra abajo:.

$ node setTimeout.js 
The queue at the McDonald's drive-through is now 6 cars long.
The queue at the McDonald's drive-through is now 0 cars long.
Quick, grab your coat!
The queue at the McDonald's drive-through is now 1 cars long.
The queue at the McDonald's drive-through is now 3 cars long.
The queue at the McDonald's drive-through is now 9 cars long.
This is beginning to look impossible!
The queue at the McDonald's drive-through is now 0 cars long.
Quick, grab your coat!
The queue at the McDonald's drive-through is now 10 cars long.
This is beginning to look impossible!

setImmediate

Si queremos que una función se ejecute con la mayor urgencia posible, usamos setImmediate. La función que ejecutamos de esta manera se ejecutará antes que todos setTimeout o setInterval llamadas tan pronto como el ciclo de eventos actual de Node.js haya terminado de llamar a las devoluciones de llamada de eventos.

Aquí hay un ejemplo de esto en proceso. Puede ejecutar este código con el comando node setImmediate.js

// setImmediate.js

// A timeout
setTimeout(function() {
    console.log('I am a timeout');
}, 5000);

// An interval
setInterval(function() {
    console.log('I am an interval');
}, 5000);

// An immediate, its callback will be executed before those defined above
setImmediate(function() {
    console.log('I am an immediate');
});

// IO callbacks and code in the normal event loop runs before the timers
console.log('I am a normal statement in the event loop, guess what comes next?');
$ node setImmediate.js 
I am a normal statement in the event loop, guess what comes next?
I am an immediate
I am a timeout
I am an interval
I am an interval
I am an interval
...

los setImmediate devolución de llamada, aunque definido después de los de setInterval y setTimeout, correrá delante de ellos.

El bucle de eventos

Una pregunta que se le podría haber ocurrido es “¿Cómo realiza Node.js un seguimiento de todos estos tiempos, temporizadores y eventos? ¿Cómo se prioriza el orden de ejecución?” Esta es una buena línea de investigación y requiere mirar algo conocido como “Bucle de eventos de Node.js”.

Entonces, ¿qué es el bucle de eventos?

los Bucle de eventos es simplemente un ciclo repetido por el cual Node.js cambia a través del procesamiento de cálculos. Dado que no puede realizar todos los cálculos posibles simultáneamente, al ser de un solo subproceso, cambia de un cálculo a otro en un ciclo bien definido conocido como ciclo de eventos.

El bucle de eventos tiene las siguientes etapas básicas:

  • Temporizadores: ejecuta devoluciones de llamada que se han programado con setTimeout y setInterval
  • Devoluciones de llamada pendientes: ejecuta las devoluciones de llamada que están listas para ejecutarse.
  • Inactivo, preparado: interno de Node.js
  • Encuesta: acepta conexiones entrantes y procesamiento de datos
  • Comprobar: invoca devoluciones de llamada configuradas mediante setImmediate
  • Cerrar devoluciones de llamada: ejecuta devoluciones de llamada para eventos cercanos

Event Loop es la columna vertebral para trabajar con eventos y otras devoluciones de llamada asincrónicas en Node.js. Nos permite colocar ganchos en ciertos puntos que serán golpeados en el transcurso del bucle.

Responder a devoluciones asincrónicas con devoluciones de llamada

Dada la naturaleza de un solo subproceso de Node.js, las operaciones de ejecución prolongada, como las lecturas de archivos o las consultas de bases de datos, se descargan rápidamente en el sistema operativo, luego Node.js continúa su ciclo de eventos de forma normal. Esto mantiene las cosas eficientes y rápidas.

¿Cómo interactúan esos procesos del sistema operativo con el proceso de Node.js? A través de devoluciones de llamada. Usamos una devolución de llamada para procesar cosas de forma asincrónica en segundo plano, luego volvemos a conectarnos al bucle de eventos una vez que se ha completado el trabajo asincrónico. Para obtener este tipo de funcionalidad en otros lenguajes de programación, puede usar una cola de tareas como Apio en Python o Sidekiq en Ruby. En Node.js, debido a que Event Loop y la ejecución asincrónica de Node.js ya ponen las cosas en cola automáticamente, obtienes este procesamiento asincrónico de forma gratuita.

Para ver las devoluciones de llamada en acción, vamos a leer un archivo del sistema de archivos y usaremos una devolución de llamada para imprimir el contenido.

El primer paso es crear el archivo. En este caso, estamos usando un archivo de texto que contiene las líneas de un poema de TS Eliot. Puede sustituir su propio archivo. Este archivo se llama poem.txt y puede colocar los siguientes contenidos en él.

// poem.txt

Macavity - The Mystery Cat, by T. S. Eliot

Macavity's a Mystery Cat: he's called the Hidden Paw--
For he's the master criminal who can defy the Law.
He's the bafflement of Scotland Yard, the Flying Squad's despair:
For when they reach the scene of crime--Macavity's not there!

Macavity, Macavity, there's no on like Macavity,
He's broken every human law, he breaks the law of gravity.
His powers of levitation would make a fakir stare,
And when you reach the scene of crime--Macavity's not there!
You may seek him in the basement, you may look up in the air--
But I tell you once and once again, Macavity's not there!

En el mismo directorio, crearemos nuestro guión que leerá este archivo de poema y lo volverá a imprimir. La impresión del archivo o el manejo de un error se logrará para nosotros en una devolución de llamada, después de que el sistema operativo devuelva el resultado de la lectura del archivo. Como se muestra a continuación, en readFile.js, su devolución de llamada se activa después de que regrese el proceso del sistema operativo asincrónico. Cuando este proceso del sistema operativo regresa, la devolución de llamada de Node.js que proporcionó se coloca en el bucle de eventos para ser procesada, que luego se ejecutará cuando el bucle llegue a ese proceso.

Su devolución de llamada puede hacer cualquier cosa, desde actualizar el estado en la aplicación, hasta manejar un error, si lo hay, y cerrar la sesión del usuario, no hacer nada o incluso terminar el proceso de node por completo.

// readFile.js

const fs = require('fs');

// Attempt to read the poem file
// Attach a callback to handle a successful read and print the contents to console
fs.readFile('./poem.txt', 'utf-8', function(err, data) {
    if (err) return console.error(err);

    let poem = data.toString();
    console.log('Here is the poem of the day...nn');
    return console.log(data);
});

Ejecute este código con node readFile.js. Se leerá el archivo y la consola debería imprimirle el poema. De lo contrario, imprimirá el error que se encontró, por ejemplo, si no existe tal archivo en la ruta especificada.

Las devoluciones de llamada son adecuadas para el manejo único de datos, errores y eventos. Sin embargo, las devoluciones de llamada pueden complicarse cuando están anidadas a varios niveles de profundidad. Otra forma alternativa de manejar eventos es utilizar Event Listeners, que se tratan en la siguiente sección.

Responder a eventos con oyentes de eventos

Los detectores de eventos son funciones que se ejecutan cuando ocurren tipos de eventos específicos. Por ejemplo, al leer un archivo, hacer una conexión a un servidor o consultar una base de datos, los módulos que utilizamos, como fs, neto mongoose, todos tienen tipos de eventos integrados que emitirán.

Los objetos que normalmente emiten estos eventos extienden la base EventEmitter objeto, que proviene del incorporado eventos módulo.

Su aplicación puede responder a estos eventos a través del mecanismo de detectores de eventos. Normalmente, adjunta un detector de eventos en el código mediante la palabra clave “on”, seguida de una cadena que especifica el tipo de evento y, finalmente, una función, que es el código que se ejecutará cuando se produzca el evento.

Para ver a los oyentes de eventos en acción, crearemos un servidor que interactúe con una API Cat y analice las respuestas de la API. Nuestro servidor atenderá las solicitudes y mostrará a los visitantes una imagen del “Gato del día”. Los eventos con los que estaremos trabajando son parte del http módulo.

También usaremos un módulo xml2js para analizar las respuestas XML que produce la API Cat. Instalar xml2js, querrás ejecutar el comando npm install xml2js en un directorio de proyectos adecuado.

Una vez que haya instalado el módulo, cree dos archivos en el directorio, cats.htmly cats.js. Dentro cats.html, coloque el front-end de nuestra aplicación. Esto simplemente mostrará los datos del gato que vamos a analizar.

<!-- cats.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Cats</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">

    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.2/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>

    <div class="container-fluid">
      <div class="col-md-8 col-md-offset-2">
        <h1>Cats Of Silicon Valley</h1>

        <h2>Welcome to the Cat Of The Day</h2>

        <img src=IMGSRC class="img-fluid" alt="Responsive image">
        <br>
        <label class="primary">Source: SOURCE</label>
        <br>
        <a href="/" class="btn btn-primary btn-lg">More Cats!</a>
      </div>
    </div>

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
  </body>
</html>

La mayor parte de nuestra lógica estará en el código del lado del servidor que funciona con los detectores de eventos. Esto está contenido en el cats.js archivo. Para ver el código del detector de eventos en acción, coloque el siguiente código dentro del archivo, luego ejecútelo con node cats.jsy, en su navegador, visite http://localhost:4000.

// cat.js

const http = require('http');
const fs = require('fs');
const xml2js = require('xml2js');

// We will get images from the CatAPI https://thecatapi.com/
let catApi = 'http://thecatapi.com/api/images/get?format=xml&results_per_page=1';

let catUrl="";
let catSource="";

let server = http.createServer(function(req, res) {
    // Get fresh cat data from the Cat API
    http.get(catApi, (res) => {
        let data="";
    
        // Attach event listener for when receiving data from the remote server is complete
        res.on('end', () => {
            console.log('***We have completed cat datan***');
            console.log(data);
      
            let parser = new xml2js.Parser();
            return parser.parseString(data, function(err, imgxml) {
                if (err) {
                    return console.log('Error parsing cat data');
                } else {
                    let imgjson = JSON.parse(JSON.stringify(imgxml));
        
                    console.log('***We have cat JSON***');
                    console.log(imgjson);
        
                    catUrl = imgjson.response.data[0].images[0].image[0].url[0];
                    return catSource = imgjson.response.data[0].images[0].image[0].source_url[0];
                }
            });
        });
    
        // Event listener for the 'data' event
        // In this case, accumulate all the data so we can use it all at once later
        return res.on('data', (xml) => {
            return data += xml;
        });
    });

    // Serve cat images from the CatAPI
    return fs.readFile('./cats.html', function(err, cathtml) {
        if (err) {
            console.error(err);
            return res.end('An error occurred');
        }
    
        let html = cathtml.toString()
                          .replace('IMGSRC', catUrl)
                          .replace('SOURCE', catSource);
    
        res.writeHead(200, {
            'Content-Type': 'text/html'
        });
    
        res.write(html);
        return res.end();
    });
});

// Run the server
server.listen(4000);

A continuación, nos adentramos en el código en detalle. También eche un vistazo a los comentarios en el código.

Como puede ver en el código, nuestra solicitud a la API de Cat solicita nuevos datos de gato. Luego dejamos que la ejecución de Node.js continúe normalmente. Sin embargo, adjuntamos dos detectores de eventos para tratar los nuevos eventos de la API remota. El primero de ellos es un detector de eventos “al final”. Cuando tenemos una carga útil de gato completa de la API de Cat, actualizamos nuestra página con los nuevos datos e imágenes. La segunda clase de evento que estamos escuchando es el evento de “datos”. Esto se activa cuando hay nuevos datos del host remoto. En ese caso, almacenamos los datos en búfer y los agregamos a nuestro almacén de datos temporal.

Ahora, gracias al poder de los oyentes de eventos, hemos facilitado la obtención de nuevas imágenes de gatos a voluntad.

Los visitantes de nuestro sitio web pueden obtener nuevas imágenes del gato del día con solo hacer clic en un botón.

Hay mucho más en eventos y temporizadores en Node.js de lo que describimos aquí. Un buen tema siguiente para explorar es el de emisores de eventos, lo que le brinda aún más poder sobre los tipos de eventos que puede utilizar su aplicación.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad