Så här gör du ett speltidsprogram i realtid med Node.js

Vad du ska skapa

I dagens artikel kommer jag att visa hur man gör en webbapplikation som visar live-spelresultat från NHL. Poängerna uppdateras automatiskt när spelen går vidare.

Detta är en mycket spännande artikel för mig, eftersom det ger mig chansen att föra två av mina favoritlustioner tillsammans: utveckling och sport.

Tekniken som ska användas för att skapa applikationen är:

  1. Node.js
  2. Socket.io
  3. MySportsFeed.com

Om du inte har Node.js installerat, besök deras hämtningssida nu och sätt på det innan du fortsätter.

Vad är Socket.io?

Socket.io är en teknik som kopplar en klient till en server. I det här exemplet är klienten en webbläsare och servern är programmet Node.js. Servern kan ha flera klienter anslutna till den vid en viss tidpunkt.

När anslutningen har upprättats kan servern skicka meddelanden till alla klienter eller en enskild klient. I gengäld kan klienten skicka ett meddelande till servern, vilket möjliggör dubbelriktad realtidskommunikation.

Innan Socket.io, brukar webbapplikationer vanligen använda AJAX, och både klienten och servern skulle undersöka varandra som letar efter händelser. Till exempel, var 10: e sekund ett AJAX-samtal skulle inträffa för att se om det fanns några meddelanden att hantera.

Polling för meddelanden orsakade en betydande mängd avkostnader på både klienten och servern eftersom det ständigt letade efter meddelanden när det inte fanns någon.

Med Socket.io mottas meddelanden omedelbart utan att behöva leta efter meddelanden, vilket minskar överkostnaden.

Sample Socket.io Application

Innan vi konsumerar sportdata i realtid, låt oss skapa ett exempelprogram för att visa hur Socket.io fungerar.

Till att börja med ska jag skapa en ny Node.js-applikation. I ett konsolfönster ska jag navigera till C: \ GitHub \ NodeJS, skapa en ny mapp för min ansökan och skapa en ny applikation:

cd \ GitHub \ NodeJS mkdir SocketExample cd SocketExample npm init

Jag använde alla standardinställningarna.

Eftersom vi gör en webbapplikation ska jag använda ett NPM-paket som heter Express för att förenkla installationen. Installera det enligt följande i en kommandotolk: npm installera express - save

Och såklart måste vi installera Socket.io-paketet: npm installera socket.io - save

Låt oss börja med att skapa webbservern. Skapa en ny fil som heter index.js och placera följande kod i den för att skapa webbservern med Express:

var app = kräver ("express") (); var http = kräver ('http'). Server (app); app.get ('/', funktion (req, res) res.sendFile (__ dirname + '/index.html');); http.listen (3000, funktion () console.log ('HTTP-server startad på port 3000'););

Om du inte är bekant med Express innehåller ovanstående kodexempel Express-biblioteket och skapar en ny HTTP-server. I detta exempel lyssnar HTTP-servern på port 3000, t.ex. http: // localhost: 3000. En rutt skapas i roten till webbplatsen "/". Resultatet av rutten returnerar en HTML-fil: index.html.

Innan vi skapar index.html-filen, låt oss avsluta servern genom att konfigurera Socket.io. Lägg till följande i din index.js-fil för att skapa socket-servern:

var io = kräver ('socket.io') (http); io.on ("anslutning", funktion (uttag) console.log ('Mottagad klientanslutning'););

På samma sätt som Express börjar koden genom att importera Socket.io-biblioteket. Detta lagras i en kallad variabel io. Därefter använder du io variabel, skapas en händelsehanterare med fungera. Händelsen som lyssnas efter är anslutning. Denna händelse heter varje gång en klient ansluter till servern.

Låt oss nu skapa vår mycket grundläggande klient. Skapa en ny fil som heter index.html och placera följande kod inom:

   Socket.IO Exempel      

HTML-filen ovan laddar Socket.io-klienten JavaScript och initierar en anslutning till servern. För att se exemplet, starta din nod-applikation: nod index.js

Sedan, i din webbläsare, navigera till http: // localhost: 3000. Ingenting kommer att visas på sidan; Om du tittar på konsolen där nodprogrammet körs loggas två meddelanden:

  1. HTTP-servern startade på port 3000
  2. Klientanslutning mottagen

Nu när vi har en framgångsrik anslutning, låt oss använda den. Låt oss börja med att skicka ett meddelande från servern till klienten. När kunden får meddelandet kan det sedan skicka ett svar tillbaka till servern.

Låt oss titta på den förkortade index.js-filen:

io.on ('connection', funktion (uttag) console.log ('Client connection received'); socket.emit ('sendToClient', hej: 'world'); socket.on ('receivedFromClient' (data) console.log (data);););

Den förra io.on funktionen har uppdaterats för att inkludera några nya kodrader. Den första, socket.emit, skickar meddelandet till klienten. De sendToClient är namnet på händelsen. Genom att namnge händelser kan du skicka olika typer av meddelanden så att klienten kan tolka dem annorlunda. Det andra tillägget är socket.on, som också innehåller ett evenemangsnamn: receivedFromClient. Detta skapar en funktion som tar emot data från klienten. I det här fallet loggas data till konsolfönstret.

Det kompletterar serverns ändringar. Det kan nu skicka och ta emot data från alla anslutna kunder.

Låt oss slutföra detta exempel genom att uppdatera klienten för att ta emot sendToClient händelse. När den tar emot händelsen kan den svara med receivedFromClient händelse tillbaka till servern.

Detta uppnås i JavaScript-delen av HTML-filen, så i index.html-filen har jag uppdaterat JavaScript på följande sätt:

var socket = io (); socket.on ('sendToClient', funktion (data) console.log (data); socket.emit ('receivedFromClient', min: 'data';);

Med hjälp av instantiated sockelvariabeln har vi mycket liknande logik på servern med en socket.on fungera. För kunden lyssnar det på sendToClient händelse. Så snart kunden är ansluten skickar servern detta meddelande. När klienten tar emot den loggas den i konsolen i webbläsaren. Klienten använder sedan samma socket.emit att servern brukade skicka den ursprungliga händelsen. I det här fallet skickar klienten tillbaka receivedFromClient händelse till servern. När servern tar emot meddelandet loggas det i konsolfönstret.

Prova själv. Först, i en konsol, kör din nodprogram: nod index.js. Ladda sedan http: // localhost: 3000 i din webbläsare.

Kontrollera webbläsarkonsolen och du bör se följande JSON-data loggade: Hej världen"

Sedan ska du se följande i kommandotolken där nodprogrammet körs:

HTTP-server startade på port 3000 Klientanslutning mottagen min: 'data'

Både klienten och servern kan använda de JSON-data som tas emot för att utföra specifika uppgifter. Vi kommer att lära oss mer om det när vi ansluter till realtids sportdata.

Sportdata

Nu när vi har behärskat hur du skickar och tar emot data till och från klienten och servern kan det här utnyttjas för att ge uppdateringar i realtid. Jag valde att använda sportdata, även om samma teori inte är begränsad till sport. Innan jag började projektet, undersökte jag olika sportdata. Den jag bestämde mig för, eftersom de erbjuder gratis utvecklare konton, var MySportsFeeds (jag är inte ansluten till dem på något sätt). För att komma åt realtidsdata, registrerade jag mig för ett konto och gjorde sedan en liten donation. Donationer börjar vid $ 1 för att uppdatera data var 10: e minut. Detta kommer att vara bra för exemplet.

När ditt konto har konfigurerats kan du fortsätta att ställa in åtkomst till deras API. För att hjälpa till med detta kommer jag att använda sitt NPM-paket: npm installera mysportsfeeds-node -save

Efter att paketet har installerats kan API-samtal göras enligt följande:

var MySportsFeeds = kräver ("mysportsfeeds-nod"); var msf = nya MySportsFeeds ("1.2", true); msf.authenticate ("********", "*********"); var idag = nytt datum (); msf.getData ('nhl', '2017-2018-regular', 'resultattavla', 'json', fordate: today.getFullYear () + ('0' + parseInt (today.getMonth () + 1)). skiva (-2) + ('0' + today.getDate ()). skiva (-2), kraft: true);

I exemplet ovan, var noga med att ersätta samtalet till autentiseringsfunktionen med ditt användarnamn och lösenord.

Följande kod kör ett API-samtal till NHLs resultattavla för idag. De fordate variabel är vad som anges idag. Jag har också satt tvinga till Sann så att ett svar alltid returneras, även om uppgifterna inte har ändrats.

Med den nuvarande inställningen får resultaten från API-samtalet skrivas till en textfil. I det sista exemplet kommer detta att ändras; För demonstrationsändamål kan resultatfilen ses över i en textredigerare för att förstå innehållet i svaret. Resultaten innehåller ett resultattavlaobjekt. Detta objekt innehåller en array som heter gameScore. Detta objekt lagrar resultatet av varje spel. Varje objekt innehåller ett barnobjekt som heter spel. Detta objekt ger information om vem som spelar.

Utanför spelobjektet finns det en handfull variabler som ger det aktuella läget i spelet. Uppgifterna ändras baserat på spelets tillstånd. Till exempel när spelet inte har startat finns det bara några få variabler som berättar att spelet inte är igång och inte har startat.

När spelet är igång tillhandahålls ytterligare uppgifter om poängen, vilken period spelet är i och hur mycket tid kvarstår. Vi kommer se detta i aktion när vi kommer till HTML för att visa spelet i nästa avsnitt.

Realtidsuppdateringar

Vi har alla bitar till pusslet, så det är dags att lägga pussel tillsammans för att avslöja den slutliga bilden. För närvarande har MySportsFeeds begränsat stöd för att skicka data till oss, så vi måste polla data från dem. Lyckligtvis vet vi att data bara ändras en gång var 10: e minut, så vi behöver inte lägga till overhead genom att polla för ändringar för ofta. När vi pollar data från dem kan vi trycka på uppdateringarna från servern till alla anslutna klienter.

För att utföra pollen använder jag JavaScript setInterval funktionen att ringa API (i mitt fall) var 10: e minut för att leta efter uppdateringar. När data tas emot skickas en händelse till alla anslutna klienter. När kunderna får evenemanget, kommer spelresultatet att uppdateras med JavaScript i webbläsaren.

MySportsFeeds kommer också att ringas när nodprogrammet startar först. Dessa data kommer att användas för alla klienter som ansluter innan de första 10 minutersintervallet. Detta lagras i en global variabel. Samma globala variabel uppdateras som en del av intervallvalet. Detta kommer att se till att när några nya kunder ansluter efter valet kommer de att få de senaste uppgifterna.

För att hjälpa till med viss kodrenlighet i huvudindex.js-filen har jag skapat en ny fil som heter data.js. Den här filen innehåller en funktion som exporteras (tillgänglig i index.js-filen) som utför det föregående samtalet till MySportsFeeds API. Här är hela innehållet i den filen:

var MySportsFeeds = kräver ("mysportsfeeds-nod"); var msf = nya MySportsFeeds ("1.2", true, null); msf.authenticate ("*******", "******"); var idag = nytt datum (); exports.getData = function () return msf.getData ('nhl', '2017-2018-regular', 'resultattavla', 'json', fordate: today.getFullYear () + ('0' + parseInt .getMonth () + 1)) skiva (-2) + ('0' + today.getDate ()). skiva (-2), kraft: true); ;

en hämta data funktionen exporteras och returnerar resultatet av samtalet, vilket i detta fall är ett löfte som kommer att lösas i huvudapplikationen.

Låt oss nu titta på det slutliga innehållet i index.js-filen:

var app = kräver ("express") (); var http = kräver ('http'). Server (app); var io = kräver ('socket.io') (http); var data = kräver ('. / data.js'); // Global variabel för att lagra de senaste NHL-resultaten var latestData; // Ladda NHL-data för när kundens första anslutning // Detta uppdateras var 10: e minut data.getData (). Då ((resultat) => latestData = result;); app.get ('/', funktion (req, res) res.sendFile (__ dirname + '/index.html');); http.listen (3000, funktion () console.log ('HTTP-server startad på port 3000');); io.on ('connection', funktion (uttag) // när klienterna ansluter, skicka senast data socket.emit ('data', latestData);); // uppdatera datasetInterval (funktion () data.getData (). då ((resultat) => // Uppdatera senaste resultat för när den nya klienten ansluter senasteData = resultat; // skicka det till alla anslutna klienter io.emit ( 'data', resultat); console.log ('Senast uppdaterad:' + ny datum ()););, 300000);

De första sju raderna av kod ovan instanserar de nödvändiga biblioteken och den globala latestData variabel. Den slutliga listan över bibliotek som används är: Express, Http Server skapad med Express, Socket.io och den tidigare nämnda data.js-filen som just skapats.

Med de nödvändigheter som tas om hand, fyller applikationen latestData för kunder som kommer att ansluta när servern startas först:

// Global variabel för att lagra de senaste NHL-resultaten var latestData; // Ladda NHL-data för när kundens första anslutning // Detta uppdateras var 10: e minut data.getData (). Då ((resultat) => latestData = result;);

Följande rader skapar en rutt för webbplatsens rotsida (http: // localhost: 3000 /) och starta HTTP-servern för att lyssna på port 3000.

Därefter är Socket.io inställd för att söka efter anslutningar. När en ny anslutning tas emot, skickar servern en händelse som heter data med innehållet i latestData variabel.

Och slutligen skapar den sista delen av koden pollningsintervallet. När intervallet uppstår, kommer latestData variabel uppdateras med resultaten från API-samtalet. Denna data avger emellertid samma datahändelse till alla kunder.

// uppdatera datasetInterval (funktion () data.getData (). då ((resultat) => // Uppdatera senaste resultat för när den nya klienten ansluter senasteData = resultat; // skicka det till alla anslutna klienter io.emit ( 'data', resultat); console.log ('Senast uppdaterad:' + ny datum ()););, 300000);

Du kanske märker att när kunden ansluter och en händelse emitteras, sänder den händelsen med hylsvariabeln. Detta tillvägagångssätt skickar endast händelsen till den anslutna klienten. Inuti intervallet, den globala io används för att avge händelsen. Detta skickar evenemanget till alla kunder.

Det kompletterar servern. Låt oss arbeta på klientens framkant. I ett tidigare exempel skapade jag en grundläggande index.html-fil som skapade klientanslutningen som skulle logga händelser från servern och skicka en tillbaka. Jag kommer att utvidga den filen till att innehålla det färdiga exemplet.

Eftersom servern skickar oss ett JSON-objekt, kommer jag att använda jQuery och utnyttja en jQuery-tillägg kallad JsRender. Detta är ett templerande bibliotek. Det låter mig skapa en mall med HTML som används för att visa innehållet i varje NHL-spel på ett enkelt och konsekvent sätt. I ett ögonblick ser du kraften i det här biblioteket. Den slutliga koden är över 40 streckkoder, så jag ska bryta ner den i mindre bitar och sedan visa hela HTML tillsammans i slutet.

Den här första delen skapar mallen som ska användas för att visa speldata:

Mallen definieras med en skriptikett. Den innehåller mallens id och en speciell skripttyp som heter text / x-jsrender. Mallen definierar en container div för varje spel som innehåller ett klassspel att tillämpa lite grundläggande styling. Inuti denna div börjar templeringen.

I nästa div visas bort och hemlag. Detta görs genom att sammanfatta staden och lagnamnet tillsammans från spelobjektet från MySportsFeed-data.

: Game.awayTeam.City är hur jag definierar ett objekt som kommer att ersättas med ett fysiskt värde när mallen görs. Denna syntax definieras av JsRender-biblioteket.

När lagen visas visas nästa del av koden en del villkorlig logik. När spelet är ospelade, en sträng kommer att utföras som spelet kommer att börja på :speltid.

När spelet inte är klart visas nuvarande poäng: Nuvarande poäng: : awayScore - : homeScore. Och äntligen, lite knepig logik för att identifiera vilken period hockeyspelet är i eller om det är i paus.

Om variabeln currentIntermission finns i resultaten, då använder jag en funktion som jag definierade kallad ordinal_suffix_of, som kommer att konvertera periodnumret att läsa: 1: a (2: a, 3, etc.) Inlämning.

När det inte går i paus söker jag efter currentPeriod värde. Detta använder också ordinal_suffix_of  för att visa att spelet är i den 1: a (2: a, 3, etc.) perioden.

Under denna, en annan funktion som jag definierade kallas tid kvar används för att omvandla antalet sekunder kvar till antalet minuter och sekunder som återstår under perioden. Till exempel: 10:12.

Kodens sista del visar slutresultatet eftersom vi vet att spelet har slutförts.

Här är ett exempel på hur det ser ut när det finns en blandning av färdiga spel, pågående spel och spel som inte har startat än (jag är inte en väldigt bra designer, så det ser ut som du kan förvänta dig när en utvecklare gör sitt eget användargränssnitt).

Nästa upp är en bit av JavaScript som skapar kontakten, hjälparfunktionerna ordinal_suffix_of och tid kvar, och en variabel som refererar till den skapade jQuery-mallen.

Den sista delen av koden är koden för att ta emot sockethändelsen och göra mallen:

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

Jag har en placeholder div med data-id. Resultatet av mallutgivningen (tmpl.render) skriver HTML-filen till den här behållaren. Vad som är riktigt snyggt är att JsRender-biblioteket kan acceptera en rad data, i det här fallet data.scoreboard.gameScore, som detererar genom varje element i arrayen och skapar ett spel per element.

Här är den slutliga HTML och JavaScript alla tillsammans:

   Socket.IO Exempel   

Starta nodprogrammet och bläddra till http: // localhost: 3000 för att se resultaten för dig själv!

Varje X-minut skickar servern en händelse till kunden. Klienten ska redrawera spelelementen med uppdaterade data. Så när du lämnar webbplatsen och tittar regelbundet på den, kommer du att se speluppdateringen när spel pågår.

Slutsats

Slutprodukten använder Socket.io för att skapa en server som klienter ansluter till. Servern hämtar data och skickar den till klienten. När klienten tar emot data kan den uppdatera skärmen sömlöst. Detta minskar belastningen på servern eftersom klienten bara utför arbete när den tar emot en händelse från servern.

Sockets är inte begränsade till en riktning; Klienten kan också skicka meddelanden till servern. När servern tar emot meddelandet kan det utföra viss bearbetning.

Chat-applikationer fungerar vanligen på detta sätt. Servern skulle få ett meddelande från klienten och sedan sändas till alla anslutna kunder för att visa att någon har skickat ett nytt meddelande.

Förhoppningsvis njöt du av den här artikeln eftersom jag hade en blast att skapa denna realtids sportapplikation för en av mina favoritsporter!