Cómo hacer una aplicación deportiva en tiempo real usando Node.js

Lo que vas a crear

En el artículo de hoy, voy a demostrar cómo hacer una aplicación web que muestre los puntajes de los juegos en vivo de la NHL. Las puntuaciones se actualizarán automáticamente a medida que progresen los juegos..

Este es un artículo muy emocionante para mí, ya que me brinda la oportunidad de unir dos de mis pasiones favoritas: el desarrollo y los deportes..

Las tecnologías que se utilizarán para crear la aplicación son:

  1. Node.js
  2. Zócalo.io
  3. MySportsFeed.com

Si no tiene instalado Node.js, visite su página de descarga ahora y configúrelo antes de continuar.

Qué es Socket.io?

Socket.io es una tecnología que conecta un cliente a un servidor. En este ejemplo, el cliente es un navegador web y el servidor es la aplicación Node.js. El servidor puede tener múltiples clientes conectados a él en un momento dado.

Una vez establecida la conexión, el servidor puede enviar mensajes a todos los clientes o a un cliente individual. A cambio, el cliente puede enviar un mensaje al servidor, lo que permite una comunicación bidireccional en tiempo real.

Antes de Socket.io, las aplicaciones web solían usar AJAX, y tanto el cliente como el servidor se encuestaban entre sí en busca de eventos. Por ejemplo, cada 10 segundos se produciría una llamada AJAX para ver si había mensajes para manejar.

El sondeo de mensajes provocó una gran cantidad de gastos generales tanto para el cliente como para el servidor, ya que buscaría mensajes constantemente cuando no había ninguno..

Con Socket.io, los mensajes se reciben instantáneamente, sin necesidad de buscarlos, lo que reduce los gastos generales..

Ejemplo de aplicación Socket.io

Antes de consumir los datos deportivos en tiempo real, creamos una aplicación de ejemplo para demostrar cómo funciona Socket.io.

Para empezar, voy a crear una nueva aplicación Node.js. En la ventana de la consola, voy a navegar a C: \ GitHub \ NodeJS, crear una nueva carpeta para mi aplicación y crear una nueva:

cd \ GitHub \ NodeJS mkdir SocketExample cd SocketExample npm init

Utilicé todas las configuraciones por defecto.

Debido a que estamos creando una aplicación web, usaré un paquete NPM llamado Express para simplificar la configuración. En un símbolo del sistema, instálelo de la siguiente manera: npm install express --save

Y, por supuesto, necesitaremos instalar el paquete Socket.io: npm instalar socket.io --save

Vamos a empezar por crear el servidor web. Cree un nuevo archivo llamado index.js y coloque el siguiente código dentro de él para crear el servidor web utilizando Express:

var app = require ('express') (); var http = require ('http'). Server (aplicación); app.get ('/', function (req, res) res.sendFile (__ dirname + '/index.html');); http.listen (3000, function () console.log ('Servidor HTTP iniciado en el puerto 3000'););

Si no está familiarizado con Express, el ejemplo de código anterior incluye la biblioteca Express y crea un nuevo servidor HTTP. En este ejemplo, el servidor HTTP está escuchando en el puerto 3000, por ejemplo, http: // localhost: 3000. Se crea una ruta en la raíz del sitio "/". El resultado de la ruta devuelve un archivo HTML: index.html.

Antes de crear el archivo index.html, terminemos el servidor configurando Socket.io. Agregue lo siguiente a su archivo index.js para crear el servidor Socket:

var io = require ('socket.io') (http); io.on ('conexión', función (socket) console.log ('Conexión de cliente recibida'););

Similar a Express, el código comienza al importar la biblioteca Socket.io. Esto se almacena en una variable llamada io. A continuación, usando el io variable, se crea un controlador de eventos con el en función. El evento que se escucha es la conexión. Este evento se llama cada vez que un cliente se conecta al servidor.

Ahora vamos a crear nuestro cliente muy básico. Cree un nuevo archivo llamado index.html y coloque el siguiente código dentro de:

   Ejemplo de Socket.IO      

El código HTML anterior carga el JavaScript de Socket.io client e inicializa una conexión al servidor. Para ver el ejemplo, inicie su aplicación Node: nodo index.js

Luego, en su navegador, navegue a http: // localhost: 3000. Nada aparecerá en la página; sin embargo, si observa la consola donde se ejecuta la aplicación Node, se registran dos mensajes:

  1. El servidor HTTP comenzó en el puerto 3000
  2. Conexión de cliente recibida

Ahora que tenemos una conexión de socket exitosa, pongámosla en uso. Comencemos enviando un mensaje desde el servidor al cliente. Luego, cuando el cliente recibe el mensaje, puede enviar una respuesta al servidor..

Veamos el archivo abreviado index.js:

io.on ('conexión', función (socket) console.log ('Conexión de cliente recibida'); socket.emit ('sendToClient', hola: 'mundo'); socket.on ('completedFromClient', función (datos) console.log (datos);););

El anterior io.on La función se ha actualizado para incluir algunas nuevas líneas de código. El primero, socket.emit, Envía el mensaje al cliente. los sendToClient Es el nombre del evento. Al nombrar eventos, puede enviar diferentes tipos de mensajes para que el cliente pueda interpretarlos de manera diferente. La segunda adición es la socket.on, que también contiene un nombre de evento: recibidoDe cliente. Esto crea una función que acepta datos del cliente. En este caso, los datos se registran en la ventana de la consola..

Eso completa las enmiendas del lado del servidor; ahora puede enviar y recibir datos de cualquier cliente conectado.

Completemos este ejemplo actualizando el cliente para recibir el sendToClient evento. Cuando recibe el evento, puede responder con la recibidoDe cliente evento de vuelta al servidor.

Esto se logra en la parte de JavaScript del código HTML, por lo que en el archivo index.html, he actualizado el JavaScript de la siguiente manera:

var socket = io (); socket.on ('sendToClient', function (data) console.log (data); socket.emit ('heldFromClient', my: 'data'););

Usando la variable socket instanciada, tenemos una lógica muy similar en el servidor con un socket.on función. Para el cliente, es escuchar el sendToClient evento. Tan pronto como el cliente está conectado, el servidor envía este mensaje. Cuando el cliente lo recibe, se registra en la consola en el navegador. El cliente entonces usa el mismo socket.emit que el servidor utiliza para enviar el evento original. En este caso, el cliente devuelve el recibidoDe cliente evento al servidor. Cuando el servidor recibe el mensaje, se registra en la ventana de la consola.

Pruébelo usted mismo. Primero, en una consola, ejecute su aplicación Node: nodo index.js. Luego cargue http: // localhost: 3000 en su navegador.

Verifique la consola del navegador web y debería ver los siguientes datos JSON registrados: Hola Mundo"

Luego, en el símbolo del sistema donde se ejecuta la aplicación Node, debería ver lo siguiente:

El servidor HTTP comenzó en el puerto 3000 Se recibió la conexión del cliente my: 'data'

Tanto el cliente como el servidor pueden usar los datos JSON recibidos para realizar tareas específicas. Aprenderemos más sobre eso una vez que nos conectemos a los datos deportivos en tiempo real.

Datos deportivos

Ahora que hemos dominado cómo enviar y recibir datos desde y hacia el cliente y el servidor, esto puede aprovecharse para proporcionar actualizaciones en tiempo real. Elegí usar datos deportivos, aunque la misma teoría no se limita a los deportes. Antes de comenzar este proyecto, investigué diferentes datos deportivos. El que me decidí, porque ofrecen cuentas de desarrollador gratuitas, fue MySportsFeeds (no estoy afiliado a ellos de ninguna manera). Para acceder a los datos en tiempo real, me registré para obtener una cuenta y luego hice una pequeña donación. Las donaciones comienzan en $ 1 para actualizar los datos cada 10 minutos. Esto será bueno para el ejemplo..

Una vez que su cuenta esté configurada, puede proceder a configurar el acceso a su API. Para ayudar con esto, voy a usar su paquete NPM: npm instala mysportsfeeds-node --save

Después de que se haya instalado el paquete, las llamadas a la API se pueden hacer de la siguiente manera:

var MySportsFeeds = require ("mysportsfeeds-node"); var msf = new MySportsFeeds ("1.2", true); msf.authenticate ("********", "*********"); var today = new Date (); msf.getData ('nhl', '2017-2018-regular', 'scoreboard', 'json', fordate: today.getFullYear () + ('0' + parseInt (today.getMonth () + 1)). slice (-2) + ('0' + today.getDate ()). slice (-2), force: true);

En el ejemplo anterior, asegúrese de reemplazar la llamada a la función de autenticación con su nombre de usuario y contraseña.

El siguiente código ejecuta una llamada a la API para obtener el marcador NHL de hoy. los fecha anterior La variable es lo que especifica hoy. También he puesto fuerza a cierto para que siempre se devuelva una respuesta, incluso cuando los datos no hayan cambiado.

Con la configuración actual, los resultados de la llamada a la API se escriben en un archivo de texto. En el ejemplo final, esto será cambiado; sin embargo, para fines de demostración, el archivo de resultados se puede revisar en un editor de texto para comprender el contenido de la respuesta. Los resultados contienen un objeto de marcador. Este objeto contiene una matriz llamada gameScore. Este objeto almacena el resultado de cada juego. Cada objeto contiene un objeto hijo llamado juego. Este objeto proporciona la información sobre quién está jugando..

Fuera del objeto del juego, hay un puñado de variables que proporcionan el estado actual del juego. Los datos cambian en función del estado del juego. Por ejemplo, cuando el juego no ha comenzado, solo hay unas pocas variables que nos dicen que el juego no está en progreso y no ha comenzado.

Cuando el juego está en progreso, se proporcionan datos adicionales sobre el puntaje, en qué período se encuentra el juego y cuánto tiempo queda. Veremos esto en acción cuando lleguemos al HTML para mostrar el juego en la siguiente sección.

Actualizaciones en tiempo real

Tenemos todas las piezas para el rompecabezas, por lo que ahora es el momento de armar el rompecabezas para revelar la imagen final. Actualmente, MySportsFeeds tiene un soporte limitado para enviarnos datos, por lo que tendremos que sondearlos. Afortunadamente, sabemos que los datos solo cambian una vez cada 10 minutos, por lo que no necesitamos agregar gastos generales al sondear los cambios con demasiada frecuencia. Una vez que los encuestamos, podemos enviar esas actualizaciones del servidor a todos los clientes conectados..

Para realizar el sondeo, utilizaré el JavaScript. setInterval Función para llamar a la API (en mi caso) cada 10 minutos para buscar actualizaciones. Cuando se reciben los datos, se envía un evento a todos los clientes conectados. Cuando los clientes reciban el evento, las puntuaciones del juego se actualizarán con JavaScript en el navegador web.

También se llamará a MySportsFeeds cuando se inicie la aplicación Node por primera vez. Estos datos se utilizarán para cualquier cliente que se conecte antes del primer intervalo de 10 minutos. Esto se almacena en una variable global. Esta misma variable global se actualiza como parte del sondeo de intervalo. Esto asegurará que cuando cualquier cliente nuevo se conecte después del sondeo, tendrá la información más reciente.

Para ayudar con la limpieza del código en el archivo principal index.js, he creado un nuevo archivo llamado data.js. Este archivo contendrá una función que se exporta (disponible en el archivo index.js) que realiza la llamada anterior a la API MySportsFeeds. Aquí están los contenidos completos de ese archivo:

var MySportsFeeds = require ("mysportsfeeds-node"); var msf = new MySportsFeeds ("1.2", true, null); msf.authenticate ("*******", "******"); var today = new Date (); exports.getData = function () return msf.getData ('nhl', '2017-2018-regular', 'scoreboard', 'json', fordate: today.getFullYear () + ('0' + parseInt (today .getMonth () + 1)). slice (-2) + ('0' + today.getDate ()). slice (-2), force: true); ;

UNA obtener datos La función se exporta y devuelve el resultado de la llamada, que en este caso es una Promesa que se resolverá en la aplicación principal.

Ahora veamos el contenido final del archivo index.js:

var app = require ('express') (); var http = require ('http'). Server (aplicación); var io = require ('socket.io') (http); var data = require ('./ data.js'); // Variable global para almacenar los últimos resultados de NHL var latestData; // Cargar los datos de la NHL para cuando la primera conexión del cliente // Esto se actualizará cada 10 minutos data.getData (). Luego ((resultado) => latestData = resultado;); app.get ('/', function (req, res) res.sendFile (__ dirname + '/index.html');); http.listen (3000, function () console.log ('Servidor HTTP iniciado en el puerto 3000');); io.on ('conexión', función (socket) // cuando los clientes se conectan, envían los últimos datos socket.emit ('data', latestData);); // actualizar datos setInterval (function () data.getData (). then ((result) => // Actualizar los últimos resultados para cuando el nuevo cliente se conecte latestData = result; // enviarlo a todos los clientes conectados io.emit ( 'datos', resultado); console.log ('Última actualización:' + nueva Fecha ()););, 300000);

Las primeras siete líneas de código de arriba ejemplifican las bibliotecas requeridas y la global últimosDatos variable. La lista final de bibliotecas utilizadas son: Express, servidor HTTP creado con Express, Socket.io y el archivo data.js mencionado anteriormente que se acaba de crear..

Con las necesidades atendidas, la aplicación rellena el últimosDatos para clientes que se conectarán cuando se inicie el servidor por primera vez:

// Variable global para almacenar los últimos resultados de NHL var latestData; // Cargar los datos de la NHL para cuando la primera conexión del cliente // Esto se actualizará cada 10 minutos data.getData (). Luego ((resultado) => latestData = resultado;);

Las siguientes líneas configuran una ruta para la página raíz del sitio web (http: // localhost: 3000 /) e inician el servidor HTTP para escuchar en el puerto 3000.

A continuación, el Socket.io está configurado para buscar conexiones. Cuando se recibe una nueva conexión, el servidor emite un evento llamado datos con el contenido del últimosDatos variable.

Y, finalmente, la parte final del código crea el intervalo de sondeo. Cuando se produce el intervalo, el últimosDatos La variable se actualiza con los resultados de la llamada a la API. Estos datos luego emiten el mismo evento de datos a todos los clientes.

// actualizar datos setInterval (function () data.getData (). then ((result) => // Actualizar los últimos resultados para cuando el nuevo cliente se conecte latestData = result; // enviarlo a todos los clientes conectados io.emit ( 'datos', resultado); console.log ('Última actualización:' + nueva Fecha ()););, 300000);

Puede notar que cuando el cliente se conecta y se emite un evento, está emitiendo el evento con la variable socket. Este enfoque enviará el evento solo a ese cliente conectado. Dentro del intervalo, lo global. io Se utiliza para emitir el evento. Esto enviará el evento a todos los clientes..

Eso completa el servidor. Vamos a trabajar en el front-end del cliente. En un ejemplo anterior, creé un archivo index.html básico que configuraba la conexión del cliente que registraría los eventos del servidor y los devolvería. Voy a extender ese archivo para que contenga el ejemplo completo..

Debido a que el servidor nos está enviando un objeto JSON, usaré jQuery y aprovecharé una extensión jQuery llamada JsRender. Esta es una biblioteca de plantillas. Me permitirá crear una plantilla con HTML que se usará para mostrar el contenido de cada juego NHL de una manera fácil de usar y consistente. En un momento, verás el poder de esta biblioteca. El código final tiene más de 40 líneas de código, así que lo dividiré en partes más pequeñas y luego mostraré el HTML completo al final..

Esta primera parte crea la plantilla que se utilizará para mostrar los datos del juego:

La plantilla se define utilizando una etiqueta de script. Contiene el ID de la plantilla y un tipo de script especial llamado texto / x-jsrender. La plantilla define un contenedor div para cada juego que contiene un juego de clase para aplicar un estilo básico. Dentro de esta div, comienza la plantilla..

En la siguiente división, se muestra el equipo visitante y el de casa. Esto se hace concatenando la ciudad y el nombre del equipo desde el objeto del juego a partir de los datos de MySportsFeed.

: game.awayTeam.City es como defino un objeto que será reemplazado por un valor físico cuando la plantilla sea renderizada. Esta sintaxis está definida por la biblioteca JsRender..

Una vez que se muestran los equipos, el siguiente fragmento de código hace algo de lógica condicional. Cuando el juego es no jugado, se emitirá una cadena en la que el juego comenzará a las :tiempo de juego.

Cuando el juego no se completa, se muestra la puntuación actual: Puntaje actual: : awayScore - : homeScore. Y, por último, un poco de lógica difícil para identificar en qué período está el juego de hockey o si está en el intermedio.

Si la variable currentIntermission se proporciona en los resultados, luego uso una función que definí llamada ordinal_suffix_of, que convertirá el número del período a leer: 1º (2º, 3º, etc.) Intermedio.

Cuando no está en el intermedio, busco el período actual valor. Esto también usa el ordinal_suffix_of  para mostrar que el juego está en el período 1º (2º, 3º, etc.).

Debajo de esto, otra función que definí llamada tiempo restante se utiliza para convertir la cantidad de segundos restantes en la cantidad de minutos y segundos restantes en el período. Por ejemplo: 10:12.

La parte final del código muestra la puntuación final porque sabemos que el juego se ha completado.

Este es un ejemplo de cómo se ve cuando hay una mezcla de juegos terminados, juegos en progreso y juegos que aún no han comenzado (no soy un muy buen diseñador, por lo que parece que esperarías cuando un desarrollador hace su propia interfaz de usuario).

El siguiente es un fragmento de JavaScript que crea el socket, las funciones auxiliares ordinal_suffix_of y tiempo restante, y una variable que hace referencia a la plantilla jQuery creada.

El último fragmento de código es el código para recibir el evento de socket y representar la plantilla:

socket.on ('data', function (data) console.log (data); $ ('# data'). html (tmpl.render (data.scoreboard.gameScore, helpers)););

Tengo un div marcador de posición con el id de los datos. El resultado de la representación de la plantilla (tmpl.render) escribe el HTML en este contenedor. Lo que es realmente bueno es que la biblioteca JsRender puede aceptar una matriz de datos, en este caso data.scoreboard.gameScore, que recorre cada elemento de la matriz y crea un juego por elemento.

Aquí está el HTML final y JavaScript todos juntos:

   Ejemplo de Socket.IO   

Inicie la aplicación Node y vaya a http: // localhost: 3000 para ver los resultados por sí mismo.!

Cada X minutos, el servidor enviará un evento al cliente. El cliente volverá a dibujar los elementos del juego con los datos actualizados. Así que cuando dejes el sitio abierto y lo mires periódicamente, verás que los datos del juego se actualizan cuando los juegos están actualmente en progreso.

Conclusión

El producto final utiliza Socket.io para crear un servidor al que los clientes se conectan. El servidor obtiene datos y los envía al cliente. Cuando el cliente recibe los datos, puede actualizar la pantalla a la perfección. Esto reduce la carga en el servidor porque el cliente solo realiza trabajo cuando recibe un evento del servidor.

Los enchufes no se limitan a una dirección; El cliente también puede enviar mensajes al servidor. Cuando el servidor recibe el mensaje, puede realizar algún procesamiento..

Las aplicaciones de chat comúnmente funcionan de esta manera. El servidor recibiría un mensaje del cliente y luego transmitiría a todos los clientes conectados para mostrar que alguien ha enviado un nuevo mensaje..

Espero que hayas disfrutado este artículo porque me divertí mucho al crear esta aplicación deportiva en tiempo real para uno de mis deportes favoritos.!