NodeJS: the good parts? A skeptic’s view (jax jax2013)


Published on

JavaScript used to be confined to the browser. But these days, it's becoming increasingly popular in server-side applications in the form of Node.js. Node.js provides event-driven, non-blocking I/O model that supposedly makes it easy to build scalable network application. In this talk you will learn about the consequences of combining the event-driven programming model with a prototype-based, weakly typed, dynamic language. We will share our perspective as a server-side Java developer who wasn’t entirely happy about JavaScript in the browser, let alone on the server. You will learn how to use Node.js effectively in modern, polyglot applications.

Watch the video:

Published in: Technology
1 Comment
No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

NodeJS: the good parts? A skeptic’s view (jax jax2013)

  1. 1. @crichardsonNodeJS: the good parts?A skeptic’s viewChris RichardsonAuthor of POJOs in ActionFounder of the original
  2. 2. @crichardsonPresentation goalHow a curmudgeonlyserver-side Java developerdiscovered an appreciationfor NodeJS and JavaScript
  3. 3. @crichardsonAbout Chris
  4. 4. @crichardson(About Chris)
  5. 5. @crichardsonAbout Chris()
  6. 6. @crichardsonAbout Chris
  7. 7. @crichardsonAbout Chris
  8. 8. @crichardsonvmc push About-ChrisDeveloper AdvocateSignup at
  9. 9. @crichardsonAgendaOverview of NodeJSBuilding a front-end server with NodeJSTaming tangled asynchronous code with promises
  10. 10. @crichardsonWhat’s NodeJS?Designed for DIRTy apps
  11. 11. @crichardsonSmall but growing rapidlyBusy!
  12. 12. @crichardsonNodeJS Hello Worldapp.js$ node app.js$ curl http://localhost:1337 a modulerequest handler
  13. 13. @crichardsonNodeJSJavaScriptReactorpatternModules
  14. 14. @crichardsonNodeJSJavaScriptReactorpatternModules
  15. 15. @crichardsonDynamic, weakly-typedDynamic:Types are associated with values - not variablesDefine new program elements at runtimeWeakly typed:Leave out arguments to methodsAccess non-existent object propertiesWeird implicit conversions: 99 == “99”!truthy and falsy valuesComprehensive tests are essential
  16. 16. @crichardsonJavaScript is object-oriented> var fred = {name: “Fred”, gender: “Male”};undefined>“Fred”> console.log("reading age=" + fred.age);reading age=undefinedundefined> fred.age = 99;99> fred{ name: Fred,gender: Male,age: 99 }> delete fred.agetrue> fred{ name: Fred, gender: Male }Unordered key-value pairsKeys = propertiesAdd propertyDelete property
  17. 17. @crichardsonJavaScript “classes”function Person(name) { = name;}Person.prototype.sayHello = function () { console.log("Hello " +; };var chris = new Person("Chris");chris.sayHello();Looks like aconstructor?!?What’s that?!?!This Java-like syntax is a messbecause JavaScript isn’t classbasedLooks familiar
  18. 18. @crichardsonoverridesJavaScript is a prototypallanguage__proto__a 99__proto__a 100b 200inheritedPrototypeXY
  19. 19. @crichardsonPrototypal code$ node> var person = {};undefined> person.sayHello = function () { console.log("Hello " +; };[Function]> var chris = Object.create(person, {name: {value: "Chris"}});undefined> var sarah = Object.create(person, {name: {value: "Sarah"}});undefined> chris.sayHello();Hello Chrisundefined> sarah.sayHello();Hello Sarahundefined> chris.sayHello = function () { console.log("Hello mate: " +; };[Function]> chris.sayHello();Hello mate: ChrisundefinedNot definedhereprototype properties
  20. 20. @crichardsonJavaScript is Functionalfunction makeGenerator(nextFunction) {var value = 0;return function() {var current = value;value = nextFunction(value);return current;};}var inc = makeGenerator(function (x) {return x + 1; });> inc()0> inc()1Pass function as anargumentReturn a functionclosure
  21. 21. @crichardsonPartial function application> var join = require("path").join;undefined> join("/x", "y")/x/y> var withinx = join.bind(undefined, "/x");undefined> withinx("y");/x/y>partially apply join
  22. 22. @crichardsonCreated in a hurry with thegoal of looking like JavaThe ‘Java...’ name creates expectations that it can’t satisfyFake classes: Hides prototypes BUT still seems weirdglobal namespacescope of vars is confusingMissing return statement = confusion‘function’ is really verbose‘this’ is dynamically scoped
  23. 23. @crichardsonJavaScript is the only way toget things done in thebrowser
  24. 24. @crichardsonStockholm syndrome“Stockholm syndrome ... is a psychologicalphenomenon in which hostages ... havepositive feelings toward their captors,sometimes to the point of defending them...”
  25. 25. @crichardsonMartin Fowler once said:"...Im one of those who despairs that alanguage with such deep flaws plays such animportant role in computation. Still theconsequence of this is that we must takejavascript seriously as a first-class languageand concentrate on how to limit the damageits flaws cause. ...."
  26. 26. @crichardsonUse just the good parts
  27. 27. @crichardsonUse a better language thatcompiles to JavaScriptTypeScriptTyped parameters and fieldsClasses and interfaces (dynamic structural typing)DartClass-based OOOptional static typingBidirectional binding with DOM elements
  28. 28. @crichardsonCoffeeScript Hello Worldhttp = require(http)class HttpRequestHandlerconstructor: (@message) ->handle: (req, res) =>res.writeHead(200, {Content-Type: text/plain})res.end(@message + n)handler = new HttpRequestHandler "Hi There from CoffeeScript"server = http.createServer(handler.handle)server.listen(1338, running at :-)Bound method
  29. 29. @crichardsonNodeJSJavaScriptReactorpatternModules
  30. 30. @crichardsonAbout the Reactor patternDefined by Doug Schmidt in 1995Pattern for writing scalable serversAlternative to thread-per-connection modelSingle threaded event loop dispatches events onhandles (e.g. sockets, file descriptors) to event handlers
  31. 31. @crichardsonReactor pattern structureEvent Handlerhandle_event(type)get_handle()Initiation Dispatcherhandle_events()register_handler(h)select(handlers)for each h in handlersh.handle_event(type)end loophandleSynchronous EventDemultiplexerselect()ownsnotifiesuseshandlers
  32. 32. @crichardsonBenefits:Separation of concerns - event handlers separatedfrom low-level mechanismMore efficient - no thread context switchingSimplified concurrency - single threaded
  33. 33. @crichardsonDrawbacks:Non-pre-emptive - handlers can’t block/take a longtimeDifficult to understand and debug - inverted flow ofcontrol
  34. 34. @crichardsonNodeJS event loop implementsthe Reactor pattern
  35. 35. @crichardsonApplication codeEvent-driven architectureNodeJS event loopBasic networking/file-system/etc.HTTP DB driver ...EventlistenerCallbackfunctionOnetimeeventsRecurringevents
  36. 36. @crichardsonGetting notified: Callbackexamplevar fs = require("fs");function statFile(path) {fs.stat(path, function (err, stat) {if (err) {console.log("Stat failed: " + path, err);throw err;}console.log("stat result=" + path, stat);});};By convention: firstparam is error objectBy convention: Lastarg is a callbackCallbacks aregood for onetimenotifications
  37. 37. @crichardsonGetting notified: eventlistenersEventEmitter class - inherit or useListener registration methods:on(eventName, listener)once(eventName, listener)Emitting eventsemit(eventName, args...)‘error’ event = special case: no listener print stack trace andexit!Good forrecurringevents
  38. 38. @crichardsonEvent listener examplevar fs = require("fs");var readStream = fs.createReadStream("events.js", {encoding: "utf-8"});// ReadStream << ReadableStream << EventEmitterreadStream.on(open, function (fd) {console.log("opened with fd=", fd);});// Node v0.10 has readable instead: this is deprecatedreadStream.on(data, function (data) {console.log("data=", data);});Register listenerRegister listener
  39. 39. @crichardsonCallback hellfunction times2(x, callback) {setTimeout(function () {callback(x * 2)}, 500);}function plus3(x, callback) {setTimeout(function (){callback(x + 3)}, 500);}function displayResult(z) {console.log("The result is=", z);}function plus3AndThenTimes2(x, callback){plus3(x, function (y) {times2(y, callback)})}plus3AndThenTimes2(10, displayResults);function sum(a, b, callback) {setTimeout(function () {callback(a + b);}, 500);}function plus3PlusTimes2(x, callback) {var p3, t2;function perhapsDone() {if (p3 & t2)sum(p3, t2, callback);};plus3(x, function (y) {p3 = y;perhapsDone();});times2(x, function (y) {t2 = y;perhapsDone();});}plus3PlusTimes2(10, displayResult);times2(plus3(x)) times2(x) + plus3(x)
  40. 40. @crichardsonLong running computationsLong running computation blocks event loop forother requestsNeed to run outside of main event loopOptions:Community: web workers threadsBuilt-in: NodeJS child processes
  41. 41. @crichardsonUsing child processesvar child = require(child_process).fork(child.js);function sayHelloToChild() {child.send({hello: "child"});}setTimeout(sayHelloToChild, 1000);child.on(message, function(m) {console.log(parent received:, m);});function kill() {child.kill();}setTimeout(kill, 2000);process.on(message, function (m) {console.log("child received message=", m);process.send({ihateyou: "you ruined my life"})});parent.jschild.jsCreate child processSend message to child
  42. 42. @crichardsonNodeJSJavaScriptReactorpatternModules
  43. 43. Core built-in modulesBasic networkingHTTP(S)FilesystemEventsTimers...
  44. 44. @crichardsonThousands of communitydeveloped modules frameworks, SQL/NoSQLdatabase, drivers, messaging, utilities...
  45. 45. @crichardsonWhat’s a module?One or more JavaScript filesOptional native code:Compiled during installationJavaScript != systems programming languagePackage.json - metadata including dependenciesexports.sayHello = function () {console.log(“Hello”);}foo.js
  46. 46. @crichardsonEasy to install$ npm install package-name
  47. 47. @crichardsonEasy to usevar http = require(“http”)var server = http.createServer...Core module ORPath to file ORmodule in node_modulesModule’s exports
  48. 48. @crichardsonDeveloping with NodeJSmodulesCore modulesCommunity modulesYour modulesApplication code
  49. 49. @crichardsonLots of modules BUT...Variable qualityMultiple incomplete MySQL drivers, e.g. withoutconnection pooling!Often abandoned...
  50. 50. @crichardsonTo summarizeNodeJSJavaScriptReactor patternModulesFlawed andmisunderstoodScalable yetcostly andoftenunnecessaryRich butvariable quality
  51. 51. @crichardsonAlternative technologiesAtmosphere - portable event deliveryNetty - asynchronous I/OVert.xSpringSource’s Reactor project...
  52. 52. @crichardsonSo why care aboutNodeJS?Easy to write scalable network servicesEasy to push events to the browserEasy to get (small) stuff doneIt has a role to play in modernapplication architecture
  53. 53. @crichardsonEvolving from a monolithicarchitecture....WARShippingServiceAccountingServiceInventoryServiceStoreFrontUI
  54. 54. @crichardson... to a micro-service architectureStore front web applicationshipping web applicationinventory web applicationAccountingServiceStoreFrontUIaccounting web applicationShippingServiceInventoryService
  55. 55. @crichardsonBrowserWARStoreFrontUIModelView ControllerPresentation layer evolution....HTML / HTTP+JavaScript
  56. 56. @crichardsonBrowser Web applicationRESTfulEndpointsModelView Controller...Presentation layer evolutionJSON-RESTHTML 5 -JavaScriptNo elaborate, server-side web frameworkrequiredEventpublisherEventsStaticcontent
  57. 57. @crichardsonNodeJS as an API gatewayBrowserService 1Service 2MessageBusHTML 5/JavaScriptSocket.ioclientEventsRESTful WSServerapplicationSocket.ioserverNode JSService 3RESTfulWS
  58. 58. @crichardsonAgendaOverview of NodeJSBuilding a front-end server with NodeJSTaming tangled asynchronous code with promises
  59. 59. @crichardsonServing static content
  60. 60. @crichardsonUsing low-level APIsvar http = require(http), path = require(path), mime = require(mime), fs = require("fs");var server = http.createServer(function (req, res) {var filePath;if (req.url == /) {filePath = public/index.html;} else {filePath = public + req.url;}fs.exists(filePath, function (exists) {if (exists) {res.writeHead(200, {"content-type": mime.lookup(path.basename(filePath)) });fs.createReadStream(filePath).pipe(res);} else {res.writeHead(404, {Content-Type: text/plain});res.write(Error 404: resource not found.);res.end();}});});server.listen(1337,;console.log(Server running at;Uses fileextensionCool!
  61. 61. @crichardsonUsing the express webframeworkvar express = require(express), http = require(http), app = express(), server = http.createServer(app);app.configure(function(){app.use(express.static(__dirname + /public));});server.listen(8081);
  62. 62. @crichardsonRESTful web services
  63. 63. @crichardsonImplementing RESTful WSwith Expressvar express = require(express),http = require(http),path = require(path);var app = express();var server = http.createServer(app);app.get(/portfolio/:userId, function (req, res) {var portfolio = retrievePortfolio(req.params.userId);res.json(portfolio);});Easy URLrouting anddestructuring
  64. 64. @crichardsonProxying to backend serverexpress = require(express)request = require(request)app = express.createServer()proxyToBackend = (baseUrl) ->(req, res) ->callback = (error, response, body) -> console.log("error=", error)originRequest = request(baseUrl + req.url, callback)req.pipe(originRequest)originRequest.pipe(res)app.get(/restaurant/*, proxyToBackend(, proxyToBackend(, proxyToBackend( a request handlerthat proxies to baseUrl
  65. 65. @crichardsonDelivering events to thebrowser
  66. 66. @crichardsonNodeJS clock exampleIncrements everysecond
  67. 67. - Server sidevar express = require(express), http = require(http), app = express(), server = http.createServer(app), io = require(;app.configure(function(){app.use(express.static(__dirname + /public));});server.listen(8081);io.sockets.on(connection, function (socket) { var counter = 0; function tick() { counter = counter + 1; socket.emit(tick, counter); }; setInterval(tick, 1000);});handle newconnectionSend tick event tobrowser every 1 secinitializes
  68. 68. - client side using theknockout.js MVVM frameworkvar socket = io.connect(location.hostname);function ClockModel() {self.ticker = ko.observable(1);socket.on(tick, function (data) {self.ticker(data);});};ko.applyBindings(new ClockModel());<html><body>The event is <span data-bind="text: ticker"></span><script src="/"></script><script src="/knockout-2.0.0.js"></script><script src="/clock.js"></script></body></html>clock.jsConnect tosocket.ioSubscribeto tick eventBind to modelUpdatemodel
  69. 69. @crichardsonDistributed NodeJS clockexampleBrowser NodeJS
  70. 70. @crichardsonAMQP Publishervar amqp = require(amqp),amqpConnection = amqp.createConnection(...),tickTockExchange;function tick() {var message = { tick: };tickTockExchange.publish("tickTock", message, {mandatory: true,contentType: "text/plain"});};amqpConnection.on(ready,function () {tickTockExchange"tickTock",options = {passive: false, type: fanout});tickTockExchange.on(open, function () { setInterval(tick, 1000) });});Connect toAMQPEnsureexchangeexistsPublishmessageInitialize timer
  71. 71. @crichardsonAMQP socket.iovar express = require(express), http = require(http), amqp = require(‘amqp’)....;server.listen(8081);...var amqpCon = amqp.createConnection(...);io.sockets.on(connection, function (socket) {function amqpMessageHandler(message, headers, deliveryInfo) {var m = JSON.parse(;socket.emit(‘tick’, m);};amqpCon.queue(“”, {},function(queue) {queue.bind(“tickTock”, “”);queue.subscribe(amqpMessageHandler);});});Connect toAMQP queueSubscribe toAMQP queueRepublishas socket.ioevent
  72. 72. @crichardsonAgendaOverview of NodeJSBuilding a front-end server with NodeJSTaming tangled asynchronous code with promises
  73. 73. @crichardsonAsync code = callback hellScenarios:Sequential: A B CFork and join: A and B CCode quickly becomes very messy
  74. 74. @crichardsonSimplifying code withPromises (a.k.a. Futures)Functions return a promise - no callback parameterA promise represents an eventual outcomeUse a library of functions for transforming andcomposing promisesPromises/A+ specification - (part of cujo.js by SpringSource) is a popularimplementation
  75. 75. @crichardsonTaming callback hell 1function times2(x) {var deferred = when.defer();setTimeout(function () {deferred.resolve(x * 2)}, 500);return deferred.promise;}times2(plus3(x)) Create a deferredReturn a promiseEventually supply a valuefunction plus3AndThenTimes2(x) {return plus3(x).then(times2);}plus3AndThenTimes2(10).then(displayResult);Transform value inpromisefunction plus3(x) {var deferred = when.defer();setTimeout(function () {deferred.resolve(x + 3) }, 500);return deferred.promise;}Simpler, almostsynchronous-style code
  76. 76. @crichardsonTaming callback hell 2function sum(a, b) {var deferred = when.defer();setTimeout(function () {deferred.resolve(a + b);}, 500);return deferred.promise;}function plus3PlusTimes2(x) {var p3 = plus3(x),t2 = times2(x);return when.join(p3, t2).spread(sum);}plus3PlusTimes2(10).then(displayResult);times2(x) + plus3(x)Combine resultsof two promisesCall with arrayelements asarguments
  77. 77. @crichardsonCalling non-promise codevar deferred = when.defer();fs.stat(path, function (err, statInfo) {if (err)deferred.reject(err);elsedeferred.resolve(statInfo);}var promise = deferred.promise;var nodefn = require("when/node/function");var promise =, path)Hidesboilerplatecode
  78. 78. @crichardsonFilesystem scanner exampleRead contents of directoryUse stat to determine if directory or fileRecurse on directoriesMerge the results
  79. 79. @crichardsonRead contents of directoryfunction findFilesInDir(dir) {var directoryContents, dir);...}Returns promise containingan array of file names
  80. 80. @crichardsonCreate absolute pathsfunction findFilesInDir(dir) {var directoryContents = ...var toAbsolute = join.bind(undefined, dir)var absolutePaths =, toAbsolute);...}Partially applyjoin()
  81. 81. @crichardsonUse stat to determine ifdirectory or filefunction findFilesInDir(dir) {var directoryContents = ...var absolutePaths = ...var statTasks =, makeStatTask);var statResults = parallel(statTasks);...}function makeStatTask(path) {return function () {function makeStatInfo(stats) {return {path: path, isdir: stats.isDirectory(),ctime: stats.ctime};}return, path).then(makeStatInfo);};}Execute stats inparallel
  82. 82. @crichardsonRecurse on directoriesfunction findFilesInDir(dir) {...var statResults = ...;var listOfListsOfFiles, processStatInfo);...}function processStatInfo(statInfo) {if (statInfo.isdir) {return findFilesInDir(statInfo.path);} else {return [statInfo.path];}}Map each statresult to a list offiles
  83. 83. @crichardsonFlatten array of arrays of filepathsfunction findFilesInDir(dir) {...var listOfListsOfFiles = ...;return when.reduce(listOfListsOfFiles,concatenateLists, []);}function concatenateLists(currentResult, value, index, total){return currentResult.concat(value);}
  84. 84. @crichardsonSummaryJavaScript is a very flawed languageThe asynchronous model is often unnecessary; veryconstraining; and adds complexityBUT despite those problemsToday, NodeJS is remarkably useful for building network-focussed components
  85. 85. @crichardsonQuestions?@crichardson chris.richardson@springsource.com - code and