• Like
Practical functional java script
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

Practical functional java script

  • 1,429 views
Published

Practical Functional JavaScript

Practical Functional JavaScript

Published in Education
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,429
On SlideShare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
21
Comments
0
Likes
2

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Practical Functional JavaScript Oliver Steele Ajax Experience Wednesday, 1 October 2008
  • 2. Teasers• AJAX is all about waiting for someone*, and remembering what you were going to do when they got back to you.• Functions : interactions :: objects : domains• You didnt really want threads anyway. (Most of the time.)* user, web server, other server, wall clock, plugin
  • 3. About Me implementing using where? what? graphics languages graphics languages Ruby bindings for “Minority Oblong Industries Report”-style interfaces ✔ ✔ BrowseGoods Entrepreneurial & Style&Share Consulting Fansnap ✔ ✔ Webtop Calendar Laszlo Systems OpenLaszlo ✔ ✔ Apple Advanced Dylan ✔ ✔ Technology Group (programming language)Apple System Software Skia ✔ ✔ (graphics library)
  • 4. About You Raise your hand if you know*: • Closures • Ruby / Smalltalk • XHR / AJAX • An AJAX framework (Prototype / jQuery / …) • Threads* none of these are required; this just helps me calibrate the talk
  • 5. Agenda• Context • Callbacks • Example Applications • Queueing & Throttling • MVC on the Client • Caching & Joining • Retries & Failover• Fundamentals • Closures (review) • Conclusions • Making Functions • Decorating Functions • Q&A • Some Idioms
  • 6. Non-Agenda • Comet, Bayeux, Gears • Frameworks* • Theory (this isnt your Monad or CPS fix) • Security (standard practices apply)* This talk should help you understand their implementation and use, but doesnt explore their APIs in any depth
  • 7. Example Applications
  • 8. BrowseGoods• Visually browse an Amazon department, graphically arranged• Server: Ruby (layout, image processing, app server)• Client: Prototype + Scriptaculous
  • 9. BrowseGoods Capabilities• Background prefetch of item maps• Background fetch of saved items• Operations on saved items are queued until initialization is complete
  • 10. Style & Share Server Architecture Client Internet Web Application Processes Catalog Cart Image Controller Controller Controller Catalog Model Product Image Request Catalog Carts Images Request Queue Queue Catalog Image Editor Updater Cart Sweeper Image ECS Client Retriever Catalog Update Process Garbage Collector Image Process Client Architecture Internet Catalog Cart Manager Search Gallery ECS Cart Server Cart Selector ECS Search Amazon ECS Amazon Image Proxy Proxy Server Servers ECS Connection Server Proxy AJAX Throttle Task Scheduler Internet ECS Server Style&Share Server
  • 11. Style & Share Capabilities• Retry with exponential backoff and failover• Explicit queues to control serialization order• Background prefetch for catalog items• Multiple queues to prioritize user interaction
  • 12. Fansnap• Visualize available seats in a venue• Seatmap is OpenLaszlo (compiled to Flash)• Widgets are in jQuery
  • 13. FanSnap Capabilities (Seatmap)• Two-way asynchronous communication between the Flash plugin and the HTML• Asynchronous communication between the Flash plugin and the server• Again, initialization is particularly challenging
  • 14. Webtop Calendar Webtop Calendar Client Data Architecture Calendar Client View Layer Event Event Model Calendar Model Observer Data Layer Event Calendar Calendar Service Cache Collection Range Event Report Cache Cache Serializer CalendarConnection Webtop Client Library Internet Webtop Server
  • 15. Webtop CalendarCapabilities (Data Model)• Synchronizes local model with server model• Local model is cache: some operations update it; others invalidate it• Race conditions, where prefetch overlaps operations that invalidate the cache
  • 16. Motivating Problem: Web MVCServer Client View View Model Model User Controller Controller
  • 17. MVC Basics
  • 18. Server:Waiting on the Client Server Client View Model User Controller
  • 19. Client:Waiting on the Server Server Client View Model Model Controller
  • 20. Client:Waiting on the User Client View Model User Controller
  • 21. Lots of Waiting• Client (server session)• Server (XHR) • Concurrency• User (UI event) • State• Future (setTimeout)
  • 22. Function Fundamentals
  • 23. What is a Function? Create Call graph • Math: rule that maps inputs to outputs Invoke Docs • Computer science: abstracted computationSpecs with effects and outputs • Source Runtime Software engineering: one of several units Register forCode Object documentation, testing, assertions, and Def’n analysis • Source code: unit of source text with Store inputs and outputs Contract Implementation • Runtime: invocable parameterized behavior Pass
  • 24. Nested Functions: Globalsfunction callback(x) { log(received " + x + ");} function request() { $.get(/request, callback);} request();  
  • 25. Nested Functions: Variablesvar callback = function(x) { log(received " + x + ");} var request = function() { $.get(/request, callback);} request();  
  • 26. Nested Functions: Function-First Stylefunction request() { function callback(x) { log(received " + x + "); } $.get(/request, callback);} request();  
  • 27. Nested Functions: Pyramid Orderfunction request() { $.get(/request, callback); function callback(x) { log(received " + x + "); }} request();  
  • 28. Nested Functions: Inlining the Functionfunction request() { $.get(/request, function callback(x) { log(received " + x + "); });} request();  
  • 29. Nested Functions: Anonymousfunction request() { $.get(/request, function (x) { log(received " + x + "); });} request(); 
  • 30. Creating Functions: Basicsfunction makeConst1() { return function() { return 1; }} function const1a() { return 1; }var const1b = function() { return 1; }var const1c = makeConst1(); log(const1a());log(const1b());log(const1c()); log(makeConst1()());  
  • 31. Creating Functions: Variable Capturefunction makeConstN(n) { return function() { return n; }} var const10 = makeConstN(10);log(const10());log(const10(100)); var const20 = makeConstN(20);log(const20());log(const20(100));  
  • 32. Creating Functions:Capturing More Variablesfunction makePlus1() { return function(x) { return x + 1; }}log(makePlus1()(10));function makePlusN(n) { return function(x) { return x + n; }}var plus10 = makePlusN(10);log(plus10(100));   
  • 33. Creating Functions: Function-Valued Parametersfunction plus1(x) { return x+1;} function twice(fn) { return function(x) { return fn(fn(x)); }} var plus2 = twice(plus1);log(plus2(10));  
  • 34. Creating Functions: Storagevar FnTable = { +1: function(n) { return n+1; }, +2: function(n) { return n+2; }}; log(FnTable[+1](10));log(FnTable[+2](10));  
  • 35. Creating Functions: Building a Registryvar FnTable = {};function register(name, fn) { FnTable[name] = fn; }function tableMethod(name) { return FnTable[name]; }function makeAdder(n) { return function(x) { return x + n; }} register(+1, makeAdder(1));register(+2, makeAdder(2));log(tableMethod(+1)(10));log(tableMethod(+2)(10));  
  • 36. Creating Functions: Manual Guardsfor (var i = -5; i < 5; i++) log(i);  function callIfPositive(fn) { return function(x) { return x > 0 ? fn(x) : undefined; }} var logIfPositive = callIfPositive(log); for (var i = -5; i < 5; i++) logIfPositive(i);  
  • 37. Creating Functions: Guard Constructionfunction guard(fn, g) { return function(x) { return g(x) ? fn(x) : undefined; }} function callIfPositive(fn) { return guard(fn, function(x) { return x > 0; });} var logIfPositive = callIfPositive(log); for (var i = -5; i < 5; i++) logIfPositive(i); 
  • 38. Closures (1)var get, set;function assignAccessors() { var x = 1; get = function() { return x; } set = function(y) { x = y; }}assignAccessors();log(get());set(10);log(get());
  • 39. Closures (2)function makeAccessors() { var x; return {get: function() { return x; }, set: function(y) { x = y; }}}var gf1 = makeAccessors();var gf2 = makeAccessors();gf1.set(10);gf2.set(20);log(gf1.get());log(gf2.get()); 
  • 40. Function Construction Idioms: Special Variables// this and arguments are specialfunction f() { logArguments(this, arguments);}f();f(a);f(a, b);  
  • 41. Function Construction Idioms: Function Call and Applyfunction f() { logArguments(this, arguments); }// these are equivalent:f(1, 2, 3);f.call(window, 1, 2, 3);f.apply(window, [1, 2, 3]);  
  • 42. Function Construction Idioms: Method Call and Applyvar obj = { f: function() { logArguments(this, arguments); }};// and so are these:obj.f(1, 2, 3);obj.f.call(obj, 1, 2, 3);obj.f.apply(obj, [1, 2, 3]);
  • 43. Function Construction Idioms: Borrowing Methods (Bad)// stealing a method the wrong wayvar o1 = {name: o1, show: function() { log(this.name); }};var o2 = {name: o2};o1.show();o2.show = o1.show;o2.show();delete o2.show;  
  • 44. Function Construction Idioms: Borrowing Methods (Better)// using apply to steal a methodvar o1 = {name: o1, show: function() { log(this.name); }};var o2 = {name: o2};o1.show();o1.show.call(o2);o1.show.apply(o2, []); 
  • 45. Function Construction Idioms: arguments is special// arguments isnt an Array, so this doesnt work:function capture() { var args = arguments.slice(0); // ...} // instead, steal the slice method from an instance of Array,// and apply it:function capture() { var args = [].slice.call(arguments, 0); // ...} // or just take it from Arrays prototypefunction capture() { var args = Array.prototype.slice.call(arguments, 0); // ...} 
  • 46. Function Construction Idioms: Wrapping Variadic Functionsfunction id1(fn) { return function(x) { return fn(x); }} function id2(fn) { return function(x, y) { return fn(x, y); }} function idn(fn) { return function() { return fn.apply(this, arguments); }}
  • 47. Function Construction Idioms: Record and Replayvar queue = [];function capture() { queue.push(Array.prototype.slice.call(arguments, 0));}function replay() { while (queue.length) fn.apply(null, queue.shift());}function fn(a, b) { log(a + + + b + = + (a+b));}capture(1,2);capture(1,3);capture(10,20);replay();
  • 48. Function Construction Idioms:Extending Function’s PrototypeFunction.prototype.twice = function() { var fn = this; return function() { return fn.call(this, fn.apply(this, arguments)); };}function plus1(x) { return x+1; }var plus2 = plus1.twice();log(plus2(10)); 
  • 49. Summary• Functions are values • Functions can be arguments, return values, array elements, property values • Functions can be created and “modified”• Argument lists can be saved, modified, and replayed
  • 50. Case Study: Callbacks Server Client <%@page <html> <?= t <meta <canvas <text <view <?xml v <%@ tag <xslt:t XML <?xml <root> <row> </row> <?xml <root> <row> </row> <?xml v <%@ tag XML <xslt:t <?xml <root> <row> </row>
  • 51. Callback Scenarii• Chained Callbacks • Conjunctive-Trigger Callbacks• Queues and Priority • Conditional Callbacks• Throttling • Outdated Responses• Caching• Timeouts• Retry and Failover
  • 52. Throttling: Unthrottledfor (var i = 0; i < 10; i++) $.get(/services/time, log);  
  • 53. Frequency Throttling: Call Sitevar gCounter = 0;function runNext() { if (gCounter < 10) { setTimeout(function() { $.get(/services/time, log); runNext(); }, 1000); gCounter++; }}runNext();  
  • 54. Frequency Throttling: Manual Wrappervar gQueue = [];var gNextTime = 0;$.throttled = function(url, k) { gQueue.push([url, k]); if (gQueue.length == 1) schedule(); function schedule() { setTimeout(function() { gNextTime = new Date().getTime() + 1000; var entry = gQueue.shift(); $.get(entry[0], entry[1]); if (gQueue.length) schedule(); }, Math.max(0, gNextTime - new Date().getTime())); }};  for (var i = 0; i < 10; i++)  $.throttled(/services/time, log);
  • 55. Frequency Throttling: Function Constructorfunction makeThrottled(fn, interval) { var queue = []; var nextTime = 0; return function() { queue.push(Array.prototype.slice.call(arguments, 0)); if (queue.length == 1) schedule(); } function schedule() { setTimeout(function() { nextTime = new Date().getTime() + interval; fn.apply(null, queue.shift()); if (queue.length) schedule(); }, Math.max(0, nextTime - new Date().getTime())); }}$.throttled = makeThrottled($.get, 1000);for (var i = 0; i < 10; i++) $.throttled(/services/time, log);  
  • 56. Count Throttling: Manual Wrappervar gQueue = [];var gOutstanding = 0;$.throttled = function(url, k) { function k2() { gOutstanding--; k.apply(this, arguments); if (gOutstanding < 2 && gQueue.length) { var entry = gQueue.shift(); $.get(entry[0], entry[1]); } } if (gOutstanding < 2) { gOutstanding++; $.get(url, k2); } else gQueue.push([url, k2]);};for (var i = 0; i < 10; i++) $.throttled(/services/sleep/2, log);  
  • 57. Count Throttling: Constructorfunction makeLimited(fn, count) { var queue = []; var outstanding = 0; return function() { var args = Array.prototype.slice.call(arguments, 0); // replace the last arg by one that runs the // next queued fn args.push(adviseAfter(args.pop(), next)); if (outstanding < count) { outstanding++; fn.apply(this, args); } else queue.push(args); } function next() { if (queue.length) fn.apply(null, queue.shift()); }}$.throttled = makeLimited($.get, 2); for (var i = 0; i < 10; i++) $.throttled(/services/sleep/2, log);
  • 58. Frequency Throttling: Function Constructorfunction makeThrottled(fn, interval) { var queue = []; var nextTime = 0; return function() { queue.push(Array.prototype.slice.call(arguments, 0)); if (queue.length == 1) schedule(); } function schedule() { setTimeout(function() { nextTime = new Date().getTime() + interval; fn.apply(null, queue.shift()); if (queue.length) schedule(); }, Math.max(0, nextTime - new Date().getTime())); }}$.throttled = makeThrottled($.get, 1000);for (var i = 0; i < 10; i++) $.throttled(/services/time, log);  
  • 59. Retry$.getWithRetry = function(url, k) { var countdown = 10; $.ajax({url:url, success:k, error:retry}); function retry() { if (--countdown >= 0) { log(retry); $.ajax({url:url, success:k, error:retry}); } }};$.getWithRetry(/services/error, log);  
  • 60. Throttled Retryvar gPageLoadTime = new Date;$.getWithRetry = function(url, k) { var countdown = 10; var delay = 1000; var nextTime = new Date().getTime() + delay; $.ajax({url:url, success:k, error:retry}); function retry() { if (--countdown >= 0) { setTimeout(function() { delay *= 1.5; log(retry@t+ + (new Date - gPageLoadTime)/1000 + s); nextTime = new Date().getTime() + delay; $.ajax({url:url, success:k, error:retry}); }, Math.max(0, nextTime - new Date().getTime())); } }};$.getWithRetry(/services/error, log);
  • 61. Failover$.getWithFailover = function(urls, k) { $.ajax({url:urls.shift(), success:k, error:retry}); function retry() { if (urls.length) $.ajax({url:urls.shift(), success:k, error:retry}); }};$.getWithFailover( [/services/error, /services/error, /services/time], log); 
  • 62. Caching (1)var gRequestCache = {};$.cachedGet = function(url, k) { if (url in gRequestCache) k(gRequestCache[url], success); else $.get(url, adviseBefore(k, function(result, status) { if (status == success) gRequestCache[url] = result; }));};$.cachedGet(/services/random, log);$.cachedGet(/services/random, log);$.cachedGet(/services/echo/1, log);$.cachedGet(/services/echo/2, log);setTimeout(function() {$.cachedGet(/services/random, log);}, 1000);  
  • 63. Caching (2)var gPendingRequests = {};var gRequestCache = {}; $.cachedGet(/services/random, log);$.cachedGet = function(url, k) { $.cachedGet(/services/random, log); if (url in gRequestCache) $.cachedGet(/services/echo/1, log); $.cachedGet(/services/echo/2, log); k(gRequestCache[url], success); $.cachedGet(/services/random, log); else if (url in gPendingRequests) gPendingRequests[url].push(k); else { var queue = [k]; gPendingRequests[url] = queue; $.get(url, function(result, status) { if (status == success) gRequestCache[url] = result; while (queue.length) queue.shift().call(this, result, status); delete gPendingRequests[url]; }); }}; 
  • 64. Caching (3)function memoizedContinuation(fn) { $.cachedGet(/services/random, log); var cache = {}; return function(key, k) { $.cachedGet(/services/random, log); if (key in cache) $.cachedGet(/services/echo/1, log); k(cache[key]); $.cachedGet(/services/echo/2, log); else $.cachedGet(/services/random, log); fn(key, k);   }}function consolidateContinuations(fn) { var queues = {}; return function(key, k) { if (key in queues) queues[key].push(k); else { var queue = queues[key] = [k]; fn(key, function(value) { while (queue.length) queue.shift().call(this, value); delete queues[key]; }); } }}$.cachedGet = consolidateContinuations(memoizedContinuation($.get));
  • 65. Summary• Functions-as-objects allow separation of concerns• Factor how, when, and whether from what• Functions are to interaction patterns as objects are to domains
  • 66. What is FP?• Functions are pure• Functions are values
  • 67. Q&A
  • 68. Thanks! – Oliver Steele http://osteele.com