C贸mo usar temporizadores y eventos en Node.js

    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.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *