Upgrade to Pro — share decks privately, control downloads, hide ads and more …

State of Node

State of Node

Entwicklungsgeschichte der Node.js Plattform. Kurzer Überblick über die aktuell unterstützten Features. Entwicklungsmuster für asynchrone Operationen wie Promises, Streams und RxJS

Sebastian Springer

June 20, 2016
Tweet

More Decks by Sebastian Springer

Other Decks in Programming

Transcript

  1. Sebastian • Sebastian Springer • aus München • arbeite bei

    MaibornWolff GmbH • https://github.com/sspringer82 • @basti_springer • JavaScript Entwickler
  2. Class Dienen zur besseren Strukturierung von Applikationen Syntactical Sugar, um

    das Prototypen-Konzept von JavaScript. Sie werden mittlerweile nativ von Node.js unterstützt.
  3. Class class Task {
 
 constructor(title) {
 this.title = title;


    this.state = 'todo';
 }
 
 complete() {
 this.state = 'done';
 }
 
 }
  4. 'use strict';
 
 var EventEmitter = require('events');
 
 class Greeter

    extends EventEmitter {
 
 greet() {
 this.emit('greeting', 'Hello World');
 }
 
 }
 
 var greeter = new Greeter();
 greeter.on('greeting', function (e) {
 console.log(e);
 });
 greeter.greet();
  5. Arrow Functions class List {
 
 constructor(tasks) {
 this.tasks =

    tasks;
 }
 
 getDone() {
 return this.tasks
 .filter(task => task.state === 'done');
 }
 
 }
  6. Arrow Functions var myObj = {
 data: false,
 init: function()

    {
 event.on('data',
 function() {
 this.data = true;
 }
 );
 }
 };
 
 myObj.init();
  7. Arrow Functions var myObj = {
 data: false,
 init: function()

    {
 eventBus.on('data', () => {
 this.data = true;
 });
 }
 };
 
 myObj.init();
  8. Destructuring var person = {name: 'Klaus', age: 42};
 var {name,

    age} = person; var arr = [1, 2, 3];
 var [one, two, three] = arr; var person = {name: 'Klaus', age: 42};
 var {name: firstname, age} = person;
  9. Scoping Global: Variablen sind überall verfügbar. Function: Variablen sind in

    der aktuellen Funktion und allen Subroutinen verfügbar. Closure: Erstellender Kontext einer Funktion und die Funktion selbst
  10. let Variable ist nur im aktuellen Block gültig. Variablen werden

    innerhalb des Block gehoistet. for (let task in tasks) {
 console.log(task);
 }
  11. Rest arguments ist böse! arguments ist kein richtiges Array-Objekt Rest

    Parameter steht für eine beliebige Anzahl von Argumenten. Argumentenliste wird in ein Array umgewandelt.
  12. Spread Der Spread Operator kann überall eingesetzt werden, wo ein

    Array in eine Argumentenliste umgewandelt werden soll. Verhält sich also ähnlich wie apply.
  13. Promises Versprechen auf die Erledigung einer asynchronen Aufgabe. Events? Gut

    für Fire and Forget, nicht aber, wenn es um Erfolg/ Misserfolg geht.
  14. Promises • Fulfill: Aktion war erfolgreich. • Reject: Aktion war

    nicht erfolgreich. • Pending: Noch kein fulfill oder reject • Settled: fulfilled oder rejected
  15. Promises var promise = new Promise(function(resolve, reject) {
 if (true)

    {
 resolve("Everything is fine");
 }
 else {
 reject("Something went wrong");
 }
 });
 
 promise.then(function success(data) {
 console.log(data);
 }, function failure(err) {
 console.log(err);
 });
  16. Promises - Chaining function async() {
 return new Promise(function (res,

    rej) {
 res(1);
 });
 }
 
 async().then(function (v) {
 return v + 1;
 }).then(function (v) {
 return v + 1;
 }).then(function (v) {
 console.log(`value: ${v + 1}`);
 });
  17. Flusssteuerung - all function async(number, timeout) {
 return new Promise(function

    (resolve, reject) {
 setTimeout(function () {
 console.log(`Promise Nr. ${number} resolved`);
 resolve(number);
 }, timeout);
 });
 }
 
 Promise.all([
 async(1, 1000),
 async(2, 2000),
 async(3, 3000)
 ]).then(function (data) {
 console.log(`all promises resolved ${data}`);
 });
  18. Flusssteuerung - race function async(number, timeout) {
 return new Promise(function

    (resolve, reject) {
 setTimeout(function () {
 console.log(`Promise Nr. ${number} resolved`);
 resolve(number);
 }, timeout);
 });
 }
 
 Promise.race([
 async(1, 1000),
 async(2, 2000),
 async(3, 3000)
 ]).then(function (data) {
 console.log(`one promise resolved ${data}`);
 });
  19. Beispiel Source: MySQL (relational DB) Step 1: Format anpassen Step

    2: Profilbilder herunterladen Sink: MongoDB (document orientated DB)
  20. Stream types • Readable: Informationen lesen (Source) • Writable: Informationen

    schreiben (Sink) • Duplex: readable und writable • Transform: (Basis: Duplex) Ausgabe wird anhand der Eingabe berechnet
  21. ReadStream var fs = require('fs');
 
 var options = {


    encoding: 'utf8',
 highWaterMark: 2
 };
 
 var stream = fs.createReadStream('input.txt', options);
 
 var chunk = 1;
 stream.on('readable', function () {
 console.log(chunk++, stream.read());
 });
  22. ReadStream Modes • Flowing Mode: Information flows automatically and as

    fast as possible. • Paused Mode (Default): Information has to be fetched via read() manually.
  23. Events • readable: Der nächste Chunk ist verfügbar • data:

    Daten werden automatisch gelesen • end: Es gibt keine weiteren Daten mehr • close: Der Stream wurde geschlossen • error: Es ist ein Fehler aufgetreten
  24. Object Mode Normalerweise werden String- und Buffer-Objekte unterstützt. Im Object

    Mode können beliebige JS-Objekte gestreamt werden. Encoding und Chunk Size werden ignoriert.
  25. "use strict";
 var Readable = require('stream').Readable;
 
 class TemperatureReader extends

    Readable {
 constructor(opt) {
 super(opt);
 this.items = 0;
 this.maxItems = 10;
 }
 _read() {
 if (this.items++ < this.maxItems) {
 this.push({
 date: new Date(2015, 9, this.items + 1),
 temp: Math.floor(Math.random() * 1000 - 273) + '°C'
 });
 } else {
 this.push(null);
 }
 }
 }
 
 var tr = new TemperatureReader({objectMode: true});
 var tempObj;
 tr.on('readable', function() {
 while (null !== (tempObj = tr.read())) {
 console.log(JSON.stringify(tempObj));
 }
 });
  26. WriteStream var ws = require('fs')
 .createWriteStream('output.txt');
 
 for (var i

    = 0; i < 10; i++) {
 ws.write(`chunk ${i}\n`);
 }
 ws.end('DONE');
  27. Events • drain: Ist der Rückgabewert von write() false, gibt

    das drain Event an, wenn der Stream wieder Daten akzeptiert • pipe/unpipe: Wird ausgelöst, sobald ein Readable Stream in diesen Stream gepiped wird.
  28. Buffering var ws = require('fs')
 .createWriteStream('output.txt');
 
 ws.write('START');
 
 ws.cork();


    
 for (var i = 0; i < 10; i++) {
 ws.write(`chunk ${i}\n`);
 }
 setTimeout(function () {
 ws.uncork();
 
 ws.end('DONE');
 }, 2000);
  29. Piping var fs = require('fs');
 
 var read = fs.createReadStream('input.txt');


    var write = fs.createWriteStream('pipe.txt');
 
 write.on('pipe', function () {
 console.log('piped!');
 });
 
 
 read.pipe(write);
  30. WriteStream "use strict";
 
 var Writable = require('stream').Writable;
 
 class

    WriteStream extends Writable {
 _write(chunk, enc, done) {
 console.log('WRITE: ', chunk.toString());
 done();
 }
 }
 
 
 var ws = new WriteStream();
 
 for (var i = 0; i < 10; i++) {
 ws.write('Hello ' + i);
 }
 ws.end();
  31. Duplex Streams Duplex Streams implementieren sowohl das Readable als auch

    das Writable Interface. Duplex Streams sind die Basis für Transform Streams.
  32. Duplex Streams var Duplex = require('stream').Duplex;
 
 class DuplexStream extends

    Duplex {
 _read() {
 ...
 }
 _write() {
 ...
 }
 }
  33. Transform Streams Transform Streams transformieren eine Eingabe nach bestimmten Regeln

    in eine Ausgabe. Sie basieren auf Duplex Streams, haben aber eine wesentlich einfachere API.
  34. Transform Streams "use strict";
 
 var fs = require('fs');
 var

    read = fs.createReadStream('input.txt');
 var write = fs.createWriteStream('transform.txt');
 
 var Transform = require('stream').Transform;
 
 class ToUpperCase extends Transform {
 _transform(chunk, encoding, callback) {
 this.push(chunk.toString().toUpperCase());
 callback();
 }
 }
 
 var toUpperCase = new ToUpperCase();
 
 read.pipe(toUpperCase)
 .pipe(write);
  35. Module Aktuell wird die Umsetzung des ES2015 Modulsystems unter Node.js

    noch diskutiert, um einen möglichst störungsfreien Übergang zu schaffen. Eine gewisse Zeit lang sollen CommonJS-Module und ES2015-Module parallel unterstützt werden.
  36. Modulsystem - Basics export function HelloWorld() {
 
 }
 


    export var name = 'Klaus'; Der Name der Variable oder Funktion wird zum Exportnamen.
  37. Modulsystem import 'jquery'; // import a module without any import

    bindings
 import $ from 'jquery'; // import the default export of a module
 import { $ } from 'jquery'; // import a named export of a module
 import { $ as jQuery } from 'jquery'; // import a named export to a different name
 
 export var x = 42; // export a named variable
 export function foo() {}; // export a named function
 
 export default 42; // export the default export
 export default function foo() {}; // export the default export as a function
 
 export { encrypt }; // export an existing variable
 export { decrypt as dec }; // export a variable as a new name
 export { encrypt as en } from 'crypto'; // export an export from another module
 export * from 'crypto'; // export all exports from another module
 // (except the default export)
 import * as crypto from 'crypto'; // import an entire module instance object
  38. Der Node Package Manager ist seit der Version 0.6.3 Teil

    von Node.js. Installation, Update und Removal von Paketen. Das zentrale Repo liegt unter npmjs.org. Aktuell gibt es > 250k Pakete. Jeder kann Pakete veröffentlichen. Jedes Paket löst seine eigenen Abhängigkeiten auf.
  39. Mittlerweile löst nicht mehr jedes Paket seine Abhängigkeiten selbst in

    einem node_modules-Verzeichnis auf. Sämtliche Pakete liegen in einem node_modules- Verzeichnis. Nur bei Versionskonflikten gibt es noch Unterverzeichnisse. Flache Pfade sind gut für Windows.
  40. #leftpad Azer Koçulu vs. Kik über die Lizenz am Kik-Paket.

    Azer unpublished kik und 272 weitere Pakete (unter anderem left-pad). Es funktionieren reihenweise Pakete nicht mehr wegen fehlender Abhängigkeiten. module.exports = leftpad; function leftpad (str, len, ch) { //convert the `str` to String str = str +''; //needn't to pad len = len - str.length; if (len <= 0) return str; //convert the `ch` to String if (!ch && ch !== 0) ch = ' '; ch = ch + ''; var pad = ''; while (true) { if (len & 1) pad += ch; len >>= 1; if (len) ch += ch; else break; } return pad + str; }
  41. #leftpad Als Resultat aus dem leftpad-Problem, dürfen Pakete 24 Stunden

    nach ihrem initialen Publish nicht mehr unpublished werden.
  42. Reactive Extensions Ein Projekt, das aktiv von Microsoft Open Technologies

    und einer Open Source Community entwickelt wird. ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences. http://reactivex.io/intro.html
  43. RxJS RxJS ist eine Implementierung der Reactive Extensions für JavaScript.

    Es gibt Implementierungen für die verschiedensten Umgebungen (z.B. C++, Python, Swift, PHP).
  44. Einbindung in Node.js var Rx = require('rx');
 var RxNode =

    require('rx-node'); $ npm install --save rx-node
  45. rx-node Das rx-node Paket erweitert den Funktionsumfang von RxJS um

    Node.js-spezifische Funktionalität wie z.B. toEventEmitter fromStream fromReadableStream …
  46. Observables 1. Observer definieren: Funktion die den Wert der asynchronen

    Operation verarbeitet
 2. Observable definieren: Asynchrone Operation definieren
 3. Observer auf das Observable subscriben
 4. Der Observer wird aufgerufen, sobald das Observable etwas sendet
  47. Observables var observer = Rx.Observer.create(
 x => console.log('Next: %s', x),

    // next
 err => console.log('Error: %s', err), //error
 () => console.log('Completed') // complete
 );
 
 var source = Rx.Observable.range(0, 3);
 
 var subscription = source.subscribe(observer);
  48. Hot vs. Cold Observables Hot: Beginnt, sobald es erstellt wurde

    Cold: Wartet, bis ein Observer sich subscribed
  49. Erstellen von Observables var source = Rx.Observable.create(function (observer) {
 observer.onNext(42);


    observer.onCompleted();
 });
 
 var source2 = Rx.Observable.range(0, 10);
 
 var source3 = RxNode.fromReadableStream(process.stdin); Neue Observables erstellen
  50. Transform - map Rx.Observable.range(0, 10)
 .map(x => x * x)


    .subscribe(x => console.log(x));
 
 // 0, 1, 4, 9, 16, 25, 36, 49, 64, 81 http://reactivex.io/documentation/operators/map.html
  51. Observables filtern var arr = [1, 2, 3, 2, 5,

    6, 4];
 
 var source = Rx.Observable.from(arr)
 .distinct() .filter(x => x % 2 === 0)
 .subscribe(x => console.log(x)); // 2, 6, 4 http://reactivex.io/documentation/operators/filter.html
  52. Scheduler Bestimmen den Ausführungskontext, in dem ein Observable Nachrichten an

    seine Observer sendet. Viele Operatoren akzeptieren einen Scheduler z.B. Rx.Observable.range(start, count, [scheduler])
 Rx.Observable.from(iterable, [mapFn], [thisArg], [scheduler])
  53. Scheduler • null: Synchrone Ausführung • Rx.Scheduler.queue: Aufgaben werden in

    einer Warteschlange angeordnet • Rx.Scheduler.asap: Aufgabe wird zum nächstmöglichen Zeitpunkt ausgeführt
 • Rx.Scheduler.async: Asynchrone Ausführung
 • requestAnimationFrame: in RxJS-DOM
  54. Scheduler - synchron var source = Rx.Observable.range(1, 3);
 
 console.log('before

    subscribe');
 source.subscribe(
 (x) => { console.log('value ', x)
 });
 console.log('after subscribe');
 // before subscribe // value 1 // value 2 // value 3 // after subscribe
  55. Scheduler - asynchron var source = Rx.Observable.range(1, 3, Rx.Scheduler.async);
 


    console.log('just before subscribe');
 source.subscribe(
 (x) => { console.log('value ', x)
 });
 console.log('just after subscribe');
 
 // before subscribe // after subscribe // value 1 // value 2 // value 3
  56. pm2

  57. pm2 $ npm install -g pm2 $ pm2 start index.js

    $ pm2 list ┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │ ├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤ │ index │ 0 │ fork │ 20049 │ online │ 0 │ 4s │ 25.109 MB │ disabled │ └──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app
  58. pm2 - cluster mode $ pm2 start index.js -i $

    pm2 list ┌──────────┬────┬─────────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │ ├──────────┼────┼─────────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤ │ index │ 0 │ cluster │ 20368 │ online │ 0 │ 70s │ 27.391 MB │ disabled │ └──────────┴────┴─────────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app $ pm2 scale index.js 2 [PM2] Scaling up application ┌──────────┬────┬─────────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │ ├──────────┼────┼─────────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤ │ index │ 0 │ cluster │ 20368 │ online │ 0 │ 108s │ 27.402 MB │ disabled │ │ index │ 1 │ cluster │ 20563 │ online │ 0 │ 0s │ 20.055 MB │ disabled │ └──────────┴────┴─────────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app