The event-driven nature of javascript – IPC2012

1,489 views

Published on

Published in: Technology
  • Be the first to comment

  • Be the first to like this

The event-driven nature of javascript – IPC2012

  1. 1. MARTIN SCHUHFUSS | SPOT-MEDIA AGTHE EVENT-DRIVENNATURE OF JAVASCRIPT
  2. 2. ÜBER MICHMARTIN SCHUHFUSS@usefulthink / Hamburger / ursprünglich PHP-Entwickler / Javascript-Nerd, Performance-Fetischist / node.js und interactive-development / Architekt und Entwickler bei spot-media
  3. 3. UND IHR?
  4. 4. JAVASCRIPT ?
  5. 5. node.js ?
  6. 6. WORUM GEHT‘S?
  7. 7. THE EVENT-DRIVEN NATURE OF JAVASCRIPT
  8. 8. SERVERSIDE JS ANDNON-BLOCKING I/O
  9. 9. THE ASYNC NATURE OF JAVASCRIPT
  10. 10. JAVASCRIPT EVENTS AND THE EVENT-LOOP
  11. 11. zOMG, CALLBACKS EVERYWHERE
  12. 12. JAVASCRIPT: THE BEST PART
  13. 13. 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()
  14. 14. FIRST-CLASS FUNCTIONS// a simple functionfunction something() { console.log("something"); }// functions assigned as valuesvar aFunction = function() { /* ... */ }, somethingElse = something;// function returning a functionfunction getFunction() { return function(msg) { console.log(msg); }}var fn = getFunction();fn("foo"); // "foo"getFunction()("foo"); // works the same way!
  15. 15. FIRST-CLASS FUNCTIONS// functions as parametersfunction call(fn) { return fn();}// passing an anonymous functioncall(function() { console.log("something");}); // "something"
  16. 16. IMMEDIATE FUNCTIONSunmittelbar nach Deklaration ausgeführte Funktionen (function __immediatelyExecuted() { console.log("something"); } ()); // in short: (function() { ... } ());
  17. 17. CLOSURES/ definieren scopes für Variablen/ ermöglichen private Variablen/ „einfrieren“ von Werten/ Ermöglichen „konfigurierbare“ Funktionen
  18. 18. CLOSURES Scoping: „private“ Variablenvar deg2rad = (function() { var RAD_PER_DEG = Math.PI/180; return function(deg) { return deg * RAD_PER_DEG; }}());console.log(RAD_PER_DEG); // undefineddeg2Rad(180); // 3.1415926…
  19. 19. CLOSURES ALLES ZUSAMMEN// 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));
  20. 20. CLOSURES „Konfigurierbare“ Funktionenvar 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));
  21. 21. CLOSURES there is no block-scope…for(var i=0; i<3; i++) { setTimeout(function() { console.log("i=" + i); }, 0);}// -> i=3, i=3, i=3 WTF?
  22. 22. CLOSURES Closures zum „einfrieren“ von Variablenfor(var i=0; i<3; i++) { (function(value) { setTimeout(function() { console.log("i=" + value); }, 0); } (i));}// -> i=0, i=1, i=2
  23. 23. NON-BLOCKING FUNCTIONS
  24. 24. ASYNC FUNCTIONS
  25. 25. ASYNC FUNCTIONSASYNC FUNCTIONS ARE JAVASCRIPTS ANSWER TO position: absolute; Jed Schmidt, getting functional with (fab) – JSConf.eu 2010
  26. 26. NON-BLOCKING FUNCTIONS / keine direkten Rückgabewerte / „Callbacks“ zur Fortsetzung des Programmablaufs / Background-Tasks übernehmen die Arbeit / keine Wartezeit innerhalb des Programms
  27. 27. NON-BLOCKING FUNCTIONS var r = new XMLHttpRequest(); r.open("GET", "/foo/bar", true); r.onreadystatechange = function () { async „return“ if (r.readyState != 4 || r.status != 200) return; console.log("Success: " + r.responseText); }; r.send(); doesn‘t block execution! console.log("Request sent!");
  28. 28. BLOCKING vs. NON-BLOCKING<?php var http = require(http);$browser = new BuzzBrowser(); http.get(http://google.com,$response = $browser function(res) { -> get(http://google.com); console.log(res); }echo $response; );echo done!; console.log(on the way!);https://github.com/kriswallsmith/Buzz http://nodejs.org/api/http.html
  29. 29. BLOCKING vs. NON-BLOCKINGWHERE IS THE DIFFERENCE?
  30. 30. BLOCKING vs. NON-BLOCKING WHERE IS THE DIFFERENCE? 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.logarithmic scale!CYCLES CPU 100 101 102 103 104 105 106 107 108 L1-Cache RAM HDD-Seek (~3 cycles, 1 ns) (~250 cycles, 83 ns) (~41,000,000 cycles, ~13.7ms)CPU-Registers L2-Cache NETWORK(~1 cycle, 0.33 ns) (~14 cycles, 4.7 ns) (~240,000,000 cycles, ~80ms) http://duartes.org/gustavo/blog/post/what-your-computer-does-while-you-wait
  31. 31. BLOCKING vs. NON-BLOCKING WHERE IS THE DIFFERENCE?<?php$browser = new BuzzBrowser();$response = $browser Der php-Prozess blockiert während -> get(http://google.com); des Aufrufes und wird erst fortgesetzt, wenn $response verfügbar ist.echo $response;echo done!;
  32. 32. BLOCKING vs. NON-BLOCKING WHERE IS THE DIFFERENCE? var http = require(http); http.get(http://google.com, function(res) { console.log(res); }node.js kann weiterarbeiten, ); während der Request im console.log(on the way!);Hintergrund bearbeitet wird.
  33. 33. BLOCKING vs. NON-BLOCKING DIFFERENT SOLUTIONS/ Threads bzw. Prozesse (apache/fcgi/…) / Event-Loops/ während ein Thread / Thread-Pools (Background-Tasks) „schläft“ können andere Threads arbeiten / Alles wesentliche passiert in nur einem Prozess/ einfacher und intuitiver zu verstehen / Gehirnjogging – wann passiert was?/ Threads und Prozesse sind leider sehr teuer.
  34. 34. INTRODUCING EVENTS
  35. 35. INTRODUCING EVENTS/ Ereignisse auf die in der Software reagiert werden kann/ beliebig viele Quellen (üblicherweise nur eine)/ beliebig viele Empfänger
  36. 36. 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, ...
  37. 37. SERVER EVENTS/ I/O-events (networking, http, filesystem, …)/ timer-events/ custom-events (emitted by modules)
  38. 38. EVENT-HANDLING: CALLBACKS / vereinfachte Event-Verarbeitung / wird anstelle eines return-Wertes aufgerufen / Konvention: function callback(err, data) {}
  39. 39. 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.
  40. 40. 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 });
  41. 41. REAL-LIFE EVENTS
  42. 42. 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
  43. 43. REAL-LIFE EVENTS Restaurant-Examplerestaurant.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); }); });});
  44. 44. REAL-LIFE EVENTS Restaurant-ExampleLOOKS COMPLICATED?
  45. 45. REAL-LIFE EVENTS the same, but with synchronous callswhile(true) { var guests = waitForGuests(); assignTable(guests); handoutMenues(guests); var order; while(order = guests.getOrder()) { guests.deliver( makeDrinks(order) ); guests.deliver( makeFood(order) ); }}
  46. 46. 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
  47. 47. REAL-LIFE EVENTS SO…
  48. 48. REAL-LIFE EVENTS async is winning!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); }); }); });});
  49. 49. 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
  50. 50. REAL-LIFE EVENTS async is winning!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); }); }); });});
  51. 51. THE EVENT-LOOP
  52. 52. THEBASIC ARCHITECTURE EVENT-LOOP2 long-running operations (I/O) are handled by worker-threads 1 WORKER the code in the main program is THREADS executed and registers event- handlers for certain events. file = fs.createReadStream(...); file.on(data, myCallback); MAIN-PROCESS
  53. 53. THEBASIC ARCHITECTURE EVENT-LOOP 3 once all code is executed, the event-loop starts WORKER EVENT-QUEUE waiting for incoming events.THREADS EVENT-LOOP file = fs.createReadStream(...); file.on(data, myCallback); MAIN-PROCESS
  54. 54. THEBASIC ARCHITECTURE EVENT-LOOP 4 background-threads eventually fire events to send data to the main-program WORKER fs.end EVENT-QUEUETHREADS http.request fs.data EVENT-LOOP file = fs.createReadStream(...); file.on(data, myCallback); MAIN-PROCESS
  55. 55. THEBASIC ARCHITECTURE EVENT-LOOP WORKER fs.end EVENT-QUEUE THREADS http.request fs.data5 for each event, the associated callbacks EVENT-LOOP are executed in the main process myCallback(data); MAIN-PROCESS
  56. 56. THEBASIC ARCHITECTURE EVENT-LOOP 5 it is possible to add timerEvent custom events to the event-queue WORKER tickEventTHREADS fs.end http.request process.nextTick(callback); setTimeout(callback, 0); MAIN-PROCESS
  57. 57. THEZUSAMMENFASSUNG EVENT-LOOP/ 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
  58. 58. THE EVENT-LOOPNEVER BLOCK THE EVENT-LOOP
  59. 59. THE EVENT-LOOP EDITIONNEVER BLOCK THE EVENT-LOOP – BROWSER $(.oh-my).on(click, function(req, res) { $.ajax(/somewhere, { will block the event-loop async: false, complete: function(jqXHR, respText) { AND the UI-Thread console.log(we‘re done!); } }); console.log(waited soo long!); is executed AFTER the the }); XHR completes everytime someone makes a synchronous XHR, god kills a kitten.
  60. 60. THE EVENT-LOOPEDITIONNEVER BLOCK THE EVENT-LOOP – SERVER var running = true; process.nextTick(function() { is never called! running = false; }); while(running) { …because this never returns // ...whatever... }
  61. 61. THE EVENT-LOOPEDITIONNEVER BLOCK THE EVENT-LOOP – SERVER var http = require(http), webserver = http.createServer(); webserver.on(request, function(req, res) { // some long-running calculation (e.g. image-processing) // or synchronous call no further request-processing res.end(finally done!); while this is running });
  62. 62. THE EVENT-LOOP ASYNC FTW!var http = require(http), asyncService = require(./asyncService), webserver = http.createServer();webserver.on(request, function(req, res) { asyncService.startLongRunningOperation(); doesn‘t block asyncService.on(completed, function() { res.end(finally done!); });});
  63. 63. THE EVENT-LOOP ASYNC FTW!// asyncService.jsvar spawn = require(child_process).spawn, EventEmitter = require(events).EventEmitter, service = new EventEmitter();service.startLongRunningOperation = function() { var child = spawn(sleep, [ 2 ]); takes 2 seconds to complete child.on(exit, function(code) { service.emit(completed); });};module.exports = service;
  64. 64. THE FTW (BROWSER-EDITION)! ASYNC EVENT-LOOP$(#foo).on(click, function(ev) { // first part of something that takes some time window.setTimeout(function() { „pushed back“, allows other events // second part... to be processed in the meantime }, 0);});
  65. 65. THE EVENT-LOOP ASYNC FTW!/ Wenns mal zu lange dauert: / child-process in node.js / web-worker im browser / „split and defer“: setTimeout(continuation, 0);
  66. 66. zOMG, CALLBACKS EVERYWHERE
  67. 67. CALLBACK-HELLrestaurant.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); }); }); });});
  68. 68. CALLBACK-HELLCOULD BE EVEN WORSE
  69. 69. CALLBACK-HELL SOLUTION: EXTRACT FUNCTIONSrestaurant.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); }); });}
  70. 70. WHAT IF FUNCTIONSDEPEND ON EACH OTHER?
  71. 71. CALLBACK-HELL USE Step()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); });https://github.com/creationix/step
  72. 72. WHAT IF WE NEED MULTIPLE RESPONSES
  73. 73. CALLBACK-HELL USE Step()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); })
  74. 74. QUESTIONS?
  75. 75. THANKS!FEEDBACK https://joind.in/7340 MARTIN SCHUHFUSSSLIDES https://speakerdeck.com/u/usefulthink @usefulthink

×