Slide 1

Slide 1 text

THE EVENT-DRIVEN NATURE OF JAVASCRIPT MARTIN SCHUHFUSS | SPOT-MEDIA AG

Slide 2

Slide 2 text

ÜBER MICH / Hamburger / ursprünglich PHP-Entwickler / Javascript-Nerd, Performance-Fetischist / node.js und interactive-development / Architekt und Entwickler bei spot-media MARTIN SCHUHFUSS @usefulthink

Slide 3

Slide 3 text

UND IHR?

Slide 4

Slide 4 text

JAVASCRIPT ?

Slide 5

Slide 5 text

node.js ?

Slide 6

Slide 6 text

WORUM GEHT‘S?

Slide 7

Slide 7 text

THE EVENT-DRIVEN NATURE OF JAVASCRIPT

Slide 8

Slide 8 text

SERVERSIDE JS AND NON-BLOCKING I/O

Slide 9

Slide 9 text

THE ASYNC NATURE OF JAVASCRIPT

Slide 10

Slide 10 text

JAVASCRIPT EVENTS AND THE EVENT-LOOP

Slide 11

Slide 11 text

zOMG, CALLBACKS EVERYWHERE

Slide 12

Slide 12 text

JAVASCRIPT: THE BEST PART

Slide 13

Slide 13 text

FIRST-CLASS FUNCTIONS / Funktionen sind Objekte / …als Parameter / …als Rückgabewert / …in Variablenzuweisungen / Es gelten keine speziellen Regeln für Funktionen / Funktionen haben auch Eigenschaften und Methoden / z.B. fn.name oder fn.call() / fn.apply()

Slide 14

Slide 14 text

FIRST-CLASS FUNCTIONS // a simple function function something() { console.log("something"); } // functions assigned as values var aFunction = function() { /* ... */ }, somethingElse = something; // function returning a function function getFunction() { return function(msg) { console.log(msg); } } var fn = getFunction(); fn("foo"); // "foo" getFunction()("foo"); // works the same way!

Slide 15

Slide 15 text

FIRST-CLASS FUNCTIONS // functions as parameters function call(fn) { return fn(); } // passing an anonymous function call(function() { console.log("something"); }); // "something"

Slide 16

Slide 16 text

IMMEDIATE FUNCTIONS (function __immediatelyExecuted() { console.log("something"); } ()); // in short: (function() { ... } ()); unmittelbar nach Deklaration ausgeführte Funktionen

Slide 17

Slide 17 text

CLOSURES / definieren scopes für Variablen / ermöglichen private Variablen / „einfrieren“ von Werten / Ermöglichen „konfigurierbare“ Funktionen

Slide 18

Slide 18 text

CLOSURES var deg2rad = (function() { var RAD_PER_DEG = Math.PI/180; return function(deg) { return deg * RAD_PER_DEG; } }()); console.log(RAD_PER_DEG); // undefined deg2Rad(180); // 3.1415926… Scoping: „private“ Variablen

Slide 19

Slide 19 text

CLOSURES // for example to create a jQuery-plugin: (function($, window) { var somethingPrivate = null; $.fn.extend({ plugin: function() { // here‘s the plugin-code return this; } }); } (this.jQuery, this)); ALLES ZUSAMMEN

Slide 20

Slide 20 text

CLOSURES var animate = (function(hasTransitions) { if(hasTransitions) { return function(params) { // animate using css-transitions }; } else { return function(params) { // animate using javascript, or better // don‘t animate at all. }; }(Modernizr.csstransitions)); „Konfigurierbare“ Funktionen

Slide 21

Slide 21 text

CLOSURES for(var i=0; i<3; i++) { setTimeout(function() { console.log("i=" + i); }, 0); } // -> i=3, i=3, i=3 there is no block-scope… WTF?

Slide 22

Slide 22 text

CLOSURES for(var i=0; i<3; i++) { (function(value) { setTimeout(function() { console.log("i=" + value); }, 0); } (i)); } Closures zum „einfrieren“ von Variablen // -> i=0, i=1, i=2

Slide 23

Slide 23 text

NON-BLOCKING FUNCTIONS

Slide 24

Slide 24 text

ASYNC FUNCTIONS

Slide 25

Slide 25 text

ASYNC FUNCTIONS ASYNC FUNCTIONS ARE JAVASCRIPTS ANSWER TO position: absolute; Jed Schmidt, getting functional with (fab) – JSConf.eu 2010

Slide 26

Slide 26 text

NON-BLOCKING FUNCTIONS / keine direkten Rückgabewerte / „Callbacks“ zur Fortsetzung des Programmablaufs / Background-Tasks übernehmen die Arbeit / keine Wartezeit innerhalb des Programms

Slide 27

Slide 27 text

NON-BLOCKING FUNCTIONS var r = new XMLHttpRequest(); r.open("GET", "/foo/bar", true); r.onreadystatechange = function () { if (r.readyState != 4 || r.status != 200) return; console.log("Success: " + r.responseText); }; r.send(); console.log("Request sent!"); doesn‘t block execution! async „return“

Slide 28

Slide 28 text

BLOCKING vs. NON-BLOCKING get('http://google.com'); echo $response; echo 'done!'; var http = require('http'); http.get('http://google.com', function(res) { console.log(res); } ); console.log('on the way!'); https://github.com/kriswallsmith/Buzz http://nodejs.org/api/http.html

Slide 29

Slide 29 text

BLOCKING vs. NON-BLOCKING WHERE IS THE DIFFERENCE?

Slide 30

Slide 30 text

CPU-Registers (~1 cycle, 0.33 ns) L1-Cache (~3 cycles, 1 ns) L2-Cache (~14 cycles, 4.7 ns) http://duartes.org/gustavo/blog/post/what-your-computer-does-while-you-wait RAM (~250 cycles, 83 ns) HDD-Seek (~41,000,000 cycles, ~13.7ms) NETWORK (~240,000,000 cycles, ~80ms) 100 101 102 103 104 105 106 107 108 CPU CYCLES if a single Instruction would take 1 second to execute, the HTTP-request to google.com would require us to wait for 8 years and more. BLOCKING vs. NON-BLOCKING WHERE IS THE DIFFERENCE? logarithmic scale!

Slide 31

Slide 31 text

BLOCKING vs. NON-BLOCKING get('http://google.com'); echo $response; echo 'done!'; WHERE IS THE DIFFERENCE? Der php-Prozess blockiert während des Aufrufes und wird erst fortgesetzt, wenn $response verfügbar ist.

Slide 32

Slide 32 text

BLOCKING vs. NON-BLOCKING var http = require('http'); http.get('http://google.com', function(res) { console.log(res); } ); console.log('on the way!'); WHERE IS THE DIFFERENCE? node.js kann weiterarbeiten, während der Request im Hintergrund bearbeitet wird.

Slide 33

Slide 33 text

BLOCKING vs. NON-BLOCKING DIFFERENT SOLUTIONS / Threads bzw. Prozesse (apache/fcgi/…) / während ein Thread „schläft“ können andere Threads arbeiten / einfacher und intuitiver zu verstehen / Threads und Prozesse sind leider sehr teuer. / Event-Loops / Thread-Pools (Background-Tasks) / Alles wesentliche passiert in nur einem Prozess / Gehirnjogging – wann passiert was?

Slide 34

Slide 34 text

INTRODUCING EVENTS

Slide 35

Slide 35 text

INTRODUCING EVENTS / Ereignisse auf die in der Software reagiert werden kann / beliebig viele Quellen (üblicherweise nur eine) / beliebig viele Empfänger

Slide 36

Slide 36 text

EVENTS IM BROWSER / UI-events: resize, scroll, ... / user interaction-events: mouse*, key*, … / timer events: setTimeout(), setInterval() / render-events: requestAnimationFrame() / resource-events: load, readystatechange, … / navigation-events: hashchange, popstate, ... / communication-events: message, ...

Slide 37

Slide 37 text

SERVER EVENTS / I/O-events (networking, http, filesystem, …) / timer-events / custom-events (emitted by modules)

Slide 38

Slide 38 text

EVENT-HANDLING: CALLBACKS / vereinfachte Event-Verarbeitung / wird anstelle eines return-Wertes aufgerufen / Konvention: function callback(err, data) {}

Slide 39

Slide 39 text

EVENT-HANDLING: EventEmitter / Event-Quelle und Dispatcher / Listener für bestimmte Events werden mit on(evName, callback) angemeldet. / Events werden mit emit() (node.js) oder trigger() bzw. triggerHandler() (jQuery) ausgelöst / Events werden nur an die Listener für das Event in dem Event-Emitter gesendet.

Slide 40

Slide 40 text

EVENT-HANDLING: EventEmitter // Beispiel EventEmitter / node.js-default var EventEmitter = require('events').EventEmitter, ee = new EventEmitter(); ee.on('food', function(data) { console.log('omnom!', data); }); ee.emit('myCustomEvent', { cookies: true }); // Beispiel jQuery var $doc = $(document); $doc.on('food', function() { console.log('omnom', data); }); $doc.triggerHandler('food', { cookies: true });

Slide 41

Slide 41 text

REAL-LIFE EVENTS

Slide 42

Slide 42 text

REAL-LIFE EVENTS Restaurant-Example / Ein Gast betritt ein Restaurant / … Kellner weist einen Tisch zu / … Kellner verteilt Speisekarten / Der Gast gibt eine Bestellung auf; der Kellner… / … gibt Getränkebestellungen an Barkeeper weiter / … gibt Essensbestellungen an Küche weiter

Slide 43

Slide 43 text

restaurant.on('newGuestEnters', function(guests) { assignTable(guests); guest.on('seated', function() { guests.handout(menue); }); // this might actually happen more than once... guests.on('order', function(order) { kitchenWorker.send(order.getMeals()); kitchenWorker.on('mealsReady', function(meals) { guest.deliver(meals); }); barkeeper.send(order.getDrinks()); barkeeper.on('drinksReady', function(drinks) { guest.deliver(drinks); }); }); }); REAL-LIFE EVENTS Restaurant-Example

Slide 44

Slide 44 text

REAL-LIFE EVENTS LOOKS COMPLICATED? Restaurant-Example

Slide 45

Slide 45 text

while(true) { var guests = waitForGuests(); assignTable(guests); handoutMenues(guests); var order; while(order = guests.getOrder()) { guests.deliver( makeDrinks(order) ); guests.deliver( makeFood(order) ); } } REAL-LIFE EVENTS the same, but with synchronous calls

Slide 46

Slide 46 text

REAL-LIFE EVENTS the same, but with synchronous calls / nur 1 Gast je Kellner / n gleichzeitige Gäste brauchen n Kellner (= Threads) / n Kellner und m>n Gäste: Warteschlange am Eingang / schlafende Kellner

Slide 47

Slide 47 text

REAL-LIFE EVENTS SO…

Slide 48

Slide 48 text

restaurant.on('newGuestEnters', function(guests) { waiter.assignTable(guests); guests.on('seated', function() { guests.handout(menue); // this might actually happen more than once... guests.on('order', function(order) { kitchenWorker.send(order.getMeals()); kitchenWorker.on('mealsReady', function(meals) { guests.deliver(meals); }); barkeeper.send(order.getDrinks()); barkeeper.on('drinksReady', function(drinks) { guests.deliver(drinks); }); }); }); }); REAL-LIFE EVENTS async is winning!

Slide 49

Slide 49 text

REAL-LIFE EVENTS async is winning! / nur 1 Kellner für n gleichzeitige Gäste / zu viele Gäste führen zu langsamerer Asuführung / Zubereitung wird von Hintergrund-Services erledigt (barkeeper/kitchenWorker) / jegliche Aktion wird über Events ausgelöst / Kellner sind nur untätig, wenn

Slide 50

Slide 50 text

restaurant.on('newGuestEnters', function(guests) { waiter.assignTable(guests); guests.on('seated', function() { guests.handout(menue); // this might actually happen more than once... guests.on('order', function(order) { kitchenWorker.send(order.getMeals()); kitchenWorker.on('mealsReady', function(meals) { guests.deliver(meals); }); barkeeper.send(order.getDrinks()); barkeeper.on('drinksReady', function(drinks) { guests.deliver(drinks); }); }); }); }); REAL-LIFE EVENTS async is winning!

Slide 51

Slide 51 text

THE EVENT-LOOP

Slide 52

Slide 52 text

THE EVENT-LOOP MAIN-PROCESS file = fs.createReadStream(...); file.on('data', myCallback); BASIC ARCHITECTURE the code in the main program is executed and registers event- handlers for certain events. 1 WORKER THREADS long-running operations (I/O) are handled by worker-threads 2

Slide 53

Slide 53 text

WORKER THREADS THE EVENT-LOOP BASIC ARCHITECTURE MAIN-PROCESS file = fs.createReadStream(...); file.on('data', myCallback); EVENT-LOOP EVENT-QUEUE once all code is executed, the event-loop starts waiting for incoming events. 3

Slide 54

Slide 54 text

WORKER THREADS THE EVENT-LOOP BASIC ARCHITECTURE MAIN-PROCESS file = fs.createReadStream(...); file.on('data', myCallback); EVENT-LOOP EVENT-QUEUE background-threads eventually fire events to send data to the main-program 4 fs.data http.request fs.end

Slide 55

Slide 55 text

WORKER THREADS THE EVENT-LOOP BASIC ARCHITECTURE MAIN-PROCESS myCallback(data); EVENT-LOOP EVENT-QUEUE http.request fs.end for each event, the associated callbacks are executed in the main process 5 fs.data fs.data

Slide 56

Slide 56 text

WORKER THREADS THE EVENT-LOOP BASIC ARCHITECTURE MAIN-PROCESS process.nextTick(callback); setTimeout(callback, 0); http.request fs.end it is possible to add custom events to the event-queue 5 tickEvent timerEvent

Slide 57

Slide 57 text

THE EVENT-LOOP ZUSAMMENFASSUNG / keine Parallelisierung / Verarbeitung in Reihenfolge der Erzeugung / alles wesentliche in nur einem Prozess / separate Prozesse für CPU-Intensive Tasks / zwingt zum Schreiben von asynchronem Code

Slide 58

Slide 58 text

THE EVENT-LOOP NEVER BLOCK THE EVENT-LOOP

Slide 59

Slide 59 text

$('.oh-my').on('click', function(req, res) { $.ajax('/somewhere', { async: false, complete: function(jqXHR, respText) { console.log('we‘re done!'); } }); console.log('waited soo long!'); }); NEVER BLOCK THE EVENT-LOOP – BROWSER EDITION THE EVENT-LOOP everytime someone makes a synchronous XHR, god kills a kitten. will block the event-loop AND the UI-Thread is executed AFTER the the XHR completes

Slide 60

Slide 60 text

var running = true; process.nextTick(function() { running = false; }); while(running) { // ...whatever... } NEVER BLOCK THE EVENT-LOOP – SERVER EDITION THE EVENT-LOOP is never called! …because this never returns

Slide 61

Slide 61 text

var http = require('http'), webserver = http.createServer(); webserver.on('request', function(req, res) { // some long-running calculation (e.g. image-processing) // or synchronous call res.end('finally done!'); }); THE EVENT-LOOP no further request-processing while this is running NEVER BLOCK THE EVENT-LOOP – SERVER EDITION

Slide 62

Slide 62 text

var http = require('http'), asyncService = require('./asyncService'), webserver = http.createServer(); webserver.on('request', function(req, res) { asyncService.startLongRunningOperation(); asyncService.on('completed', function() { res.end('finally done!'); }); }); ASYNC FTW! THE EVENT-LOOP doesn‘t block

Slide 63

Slide 63 text

// asyncService.js var spawn = require('child_process').spawn, EventEmitter = require('events').EventEmitter, service = new EventEmitter(); service.startLongRunningOperation = function() { var child = spawn('sleep', [ 2 ]); child.on('exit', function(code) { service.emit('completed'); }); }; module.exports = service; ASYNC FTW! THE EVENT-LOOP takes 2 seconds to complete

Slide 64

Slide 64 text

$('#foo').on('click', function(ev) { // first part of something that takes some time window.setTimeout(function() { // second part... }, 0); }); ASYNC FTW (BROWSER-EDITION)! THE EVENT-LOOP „pushed back“, allows other events to be processed in the meantime

Slide 65

Slide 65 text

ASYNC FTW! THE EVENT-LOOP / Wenns mal zu lange dauert: / child-process in node.js / web-worker im browser / „split and defer“: setTimeout(continuation, 0);

Slide 66

Slide 66 text

zOMG, CALLBACKS EVERYWHERE

Slide 67

Slide 67 text

CALLBACK-HELL restaurant.on('newGuestEnters', function(guests) { assignTable(guests); guests.on('seated', function() { guests.handout(menue); // this might actually happen more than once... guests.on('order', function(order) { kitchenWorker.send(order.getMeals()); kitchenWorker.on('mealsReady', function(meals) { guests.deliver(meals); }); barkeeper.send(order.getDrinks()); barkeeper.on('drinksReady', function(drinks) { guests.deliver(drinks); }); }); }); });

Slide 68

Slide 68 text

CALLBACK-HELL COULD BE EVEN WORSE

Slide 69

Slide 69 text

restaurant.on('newGuestEnters', function(guests) { assignTable(guests); guests.on('seated', function() { guests.handout(menue); attachOrderHandling(guests); }); }); function attachOrderHandling(guests) { guests.on('order', function(order) { kitchenWorker.send(order.getMeals()); kitchenWorker.on('mealsReady', function(meals) { guests.deliver(meals); }); barkeeper.send(order.getDrinks()); barkeeper.on('drinksReady', function(drinks) { guests.deliver(drinks); }); }); } CALLBACK-HELL SOLUTION: EXTRACT FUNCTIONS

Slide 70

Slide 70 text

WHAT IF FUNCTIONS DEPEND ON EACH OTHER?

Slide 71

Slide 71 text

Step( function readSelf() { fs.readFile(__filename, this); }, function capitalize(err, text) { if(err) { throw err; } return text.toUpperCase(); }, function showIt(err, newText) { if(err) { throw err; } console.log(newText); } ); CALLBACK-HELL USE Step() https://github.com/creationix/step

Slide 72

Slide 72 text

WHAT IF WE NEED MULTIPLE RESPONSES

Slide 73

Slide 73 text

Step( // Loads two files in parallel function loadStuff() { fs.readFile(__filename, this.parallel()); fs.readFile("/etc/passwd", this.parallel()); }, // Show the result when done function showStuff(err, code, users) { if (err) throw err; console.log(code); console.log(users); } ) CALLBACK-HELL USE Step()

Slide 74

Slide 74 text

QUESTIONS?

Slide 75

Slide 75 text

THANKS! FEEDBACK https://joind.in/7340 MARTIN SCHUHFUSS @usefulthink SLIDES https://speakerdeck.com/u/usefulthink