code { display: inline !important; } Dieser Beitrag ist Teil einer Serie von ES2015-Beiträgen. Wir werden jede Woche über neue JavaScript-Funktionen berichten! In mehreren früheren Beiträgen haben wir uns mit Babel beschäftigt, um unseren (transpilierten) Code auszuführen. Aber wie weit können wir kommen, wenn wir nur die nativ unterstützten Funktionen verwenden? Schauen wir uns die stabilen Funktionen von Node.js 4 an und sehen wir uns an, wie viel von unserem Code ohne eine Transpiler-Abhängigkeit geschrieben werden kann.
Das Setup für den Prototyp ist ein ganz einfacher HTTP-Server, der verschiedene Inhalte unter 3 Pfaden bereitstellt:
- Der Wurzelpfad
/dient einer einfachen Nachricht als Körper - Der asynchrone Pfad
/asyncbedient eine andere Nachricht und simuliert einen asynchronen Aufruf eines anderen Systems - Andere Pfade geben einen 404-Code aus.
Wir werden keine http-Frameworks von Drittanbietern in unseren Code einbauen und uns aus Gründen der Übersichtlichkeit auf die von Node.js bereitgestellte http-Server-Implementierung verlassen. Lassen Sie uns eine Server-Implementierung unter Verwendung des aktuellen Node.js schreiben. Anhand der Liste der unterstützten Funktionen können wir die folgenden nativen ES2015-Konstrukte sicher verwenden:
constundlet- Pfeilfunktionen
- Generatorfunktionen
PromiseMap- Vorlage Zeichenfolgen
- ... und 'ein paar' andere, die 53% der offiziellen ECMA-Spezifikation implementieren
Server
Wir beginnen mit der Initialisierung unserer Server-Implementierung.
[javascript title="server.js:"]
'use strict';
const http = require('http'),
co = require('co'),
sleep = require('./sleep');
[/javascript]
Das erste Problem: um let und const in unserem Code verwenden zu können, müssen wir die 'use strict' Zeichenkette "prolog" zu unseren Dateien hinzufügen. Andernfalls werden wir mit einem co lib, um schöneren asynchronen Code mit Generatoren zu schreiben, wir werden darauf zurückkommen. Für diese Demonstration haben wir außerdem eine asynchrone Funktion sleep geschrieben, die eine Promise zurückgibt, die für eine bestimmte Zeit schläft und dann mit dem angegebenen Ergebnis aufgelöst wird:
[javascript title="sleep.js:"]
'use strict';
module.exports = (ms, result) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(result);
}, ms);
[/javascript] });
};
Mit dieser sleep Funktion simulieren wir einen asynchronen Aufruf an z.B. eine Datenbank und deren Antwort. Wir können die Pfeilfunktionen wie erwartet als Export bereitstellen. Außerdem benötigen wir keine Versprechensbibliothek, sondern verwenden direkt die native Klasse Promise.
Kehren wir zu unserer Serverimplementierung zurück.
[javascript title="server.js, Fortsetzung:" firstline="7"]
const routeHandlers = new Map();
routeHandlers.set('/', (req, res) => {
res.writeHead(200);
res.end('okay');
});
[/javascript]
Für unsere Routenbehandlung verwenden wir ein Map. Der Vorteil gegenüber der Verwendung eines Objektliteral ist, dass es keine vererbten Eigenschaften gibt, die unser Routing beeinflussen könnten.
Die Einrichtung der Routenbehandlung ist wiederum sehr einfach. Dieser spezielle Handler antwortet einfach mit einer 200-Antwort mit dem Body 'okay'. Die Pfeilfunktion sorgt für eine bessere Lesbarkeit. Natürlich sollten Sie keinen eigenen Router erstellen, sondern bei Bedarf eines der vielen http-Frameworks mit funktionsreichen Routern verwenden.
Nun zur asynchronen Aufrufbehandlung. Wir stellen uns ein gefälschtes zugrundeliegendes System vor, das von uns zwei Aufrufe verlangt: einen, um einen bestimmten Wert abzurufen, und einen zweiten Aufruf, der die erste Ausgabe verwendet und die endgültige Ausgabe liefert. Beide dieser Aufrufe geben ein Promise Objekt zurück. Wir könnten den Code mit then und catch wie folgt schreiben:
[javascript light="true"]
routeHandlers.set('/async', (req, res) => {
function errorHandler() {
res.writeHead(500);
res.end('internal error');
}
sleep(1000, 123).then((data) => {
const a = data;
sleep(2000, a + 456).then((data) => {
const b = data;
res.end(asynchronous result: ${a} ${b});
}).catch(errorHandler);
}).catch(errorHandler);
});
[/javascript]
Wir müssen eine generische Fehlerfunktion für beide Aufrufe zur Verfügung stellen, um Fehler abzufangen, was eine Art Boilerplate ist. Und vor allem die Verschachtelung der beiden then -Aufrufe macht den Codefluss schwieriger zu verstehen.
Wenn wir die Hilfsfunktion co verwenden, können wir eine Generatorfunktion und yield als 'wartendes' Konstrukt verwenden und der Code ist klarer:
[javascript title="server.js, Fortsetzung:" firstline="14"]
routeHandlers.set('/async', (req, res) => {
co(function *() {
const a = yield sleep(1000, 123);
const b = yield sleep(2000, a + 456);
res.end(asynchronous result: ${a} ${b});
}).catch(() => {
res.writeHead(500);
res.end('interner Fehler');
[/javascript] });
});
Ein zusätzlicher Vorteil ist, dass wir nur eine catch Funktion schreiben müssen, um beide Fehlerpfade abzufangen. Diese Idee der Verwendung von Generatoren und Yield wird im koa http-Framework ausgiebig genutzt. Es scheint sicherlich eine gute Kombination mit Node.js 4 zu sein.
(Haben Sie gesehen, wie wir einen Template-String verwendet haben, um unser Seitenergebnis zu rendern? Wahnsinn.)
Wir fahren mit der Erstellung des Servers fort:
[javascript title="server.js, Fortsetzung:" firstline="26"]
const server = http.createServer((req, res) => {
if (routeHandlers.has(req.url)) {
return routeHandlers.get(req.url)(req, res);
}
res.writeHead(404);
res.end('not found');
});
[/javascript]
Dies prüft im Grunde genommen, ob eine Route genau mit einer der in Map definierten Routen übereinstimmt und delegiert die Bearbeitung an die angegebene Funktion.
Wir wollen die listen und close des Servers offenlegen, aber keine anderen Funktionen:
[javascript title="server.js, Fortsetzung:" firstline="35"]
exports.listen = function () {
server.listen.apply(server, arguments);
};
exports.close = function () {
server.close.apply(server, arguments);
};
[/javascript]
Ein weiteres Problem: Wir können die Syntax der Pfeilfunktionen in diesem Fall nicht verwenden. Wir wollen das arguments Array anwenden, und das ist innerhalb von Pfeilfunktionen nicht vorgesehen.
Wenn wir also unseren Server starten, können wir sehen, dass alles wie erwartet funktioniert. Großartig! Aber was ist mit einem Test?
Test
Zum Testen verwenden wir das Mocha Test Framework und die Chai Assertion Library. Lassen Sie uns den Server-Test initialisieren:
[javascript title="server.test.js:"]
'use strict';
const server = require('../src/server'),
getBody = require('./getBody'),
co = require('co'),
assert = require('assert'),
port = 6000;
describe('server', () => {
before(() => {
server.listen(port);
});
[/javascript]
Wir können die Pfeilfunktionssyntax für unsere describe und before Aufrufe verwenden, was sehr schön ist. Die Funktion getBody gibt eine Promise zurück, die den Body-Inhalt (und die ursprüngliche Antwort) eines http get-Aufrufs auflöst:
[javascript title="getBody.js:"]
'use strict';
const http = require('http');
module.exports = (uri) => {
return new Promise((resolve) => {
const bodyChunks = [];
http.get(uri, (res) => {
res.on('data', (chunk) => {
bodyChunks.push(chunk);
}).on('end', () => {
resolve({ res, body: bodyChunks.join() });
});
});
});
});
[/javascript]
Wir können eine const bodyChunks weil das Array selbst konstant ist, aber wir können den Inhalt trotzdem manipulieren. Wir können res in das Objektliteral einfügen, indem wir die Syntax für Shorthand-Eigenschaften verwenden, um etwas weniger Code zu schreiben.
Kehren wir zu unserem Test zurück:
[javascript title="server.test.js, Fortsetzung:" firstline="14"]
describe('/', () => {
it('sollte 200 zurückgeben und mit okay antworten', (done) => {
co(function () {
const ret = yield getBody(https://xebia.com/blog:${port});
assert.equal(200, ret.res.statusCode);
assert.equal('okay', ret.body);
done();
});
});
});
[/javascript]
Hier verwenden wir co wieder, um leicht verständlichen asynchronen Code zu schreiben. Wir können eine Bibliothek wie res und Konstanten destrukturieren.
Jetzt testen wir unseren länger laufenden asynchronen Aufruf:
[javascript title="server.test.js, Fortsetzung:" firstline="25"]
describe('/async', () => {
it('sollte 200 zurückgeben und mit Nachricht antworten', function (done) {
this.timeout(4000);
co(function () {
const ret = yield getBody(https://xebia.com/blog:${port}/async);
assert.equal(200, ret.res.statusCode);
assert.equal('asynchrones Ergebnis: 123 579', ret.body);
done();
[/javascript] });
});
});
Hier müssen wir den Standard-Timeout (2000ms) für Tests ändern. Dies kann für eine einzelne it mit this.timeout gemacht werden. Aber in einer Pfeilfunktion ist this nicht verfügbar, so dass wir die vollständige function Syntax schreiben müssen.
Der Rest des Tests wird weggelassen, da er trivial ist.
Fazit
Wir haben gesehen, dass es bereits möglich ist, einige anspruchsvolle ES2015-Programme ohne die Verwendung eines Transpilers zu schreiben, wenn wir Node.js auf 4+ aktualisieren, abgesehen von ein paar Schwierigkeiten und einigen Integrationsproblemen. Kein Transpiler bedeutet auch ein besseres Debugging und keine Source-Maps mehr, das kann also ein überzeugendes Argument für Ihr nächstes Projekt sein.npm start starten und die Mocha-Testsuite mit npm test ausführen. Natürlich müssen Sie sicherstellen, dass Sie Node.js 4 als Standard eingestellt haben.
Verfasst von

Albert Brand
Unsere Ideen
Weitere Blogs
Contact



