View
9.711
Download
4
Category
Preview:
Citation preview
JscexWrite Sexy JavaScript
Zhao Jie - SNDA - May 2011
About Me
• 赵劼 / 老赵 / Jeffrey Zhao / 赵姐夫
• Programmer
• Blogger - http://blog.zhaojie.me/
• Twitter: @jeffz_cn
• F#, JavaScript, Scala, C#, Python, .NET, Mono...
• Java (as a language) hater
What’s Jscex
• JavaScript Computation EXpression
• A JavaScript language extension to help programming in common scenarios
• A port of F# Computation Expression• Inspired by the idea
• Design for JavaScript
What’s NOT Jscex
• Another language• Jscex is 100% JavaScript
• A framework• It’s a library, works with any libraries / frameworks
• A JavaScript engine / runtime• Execute in any ECMAScript 3 engines
Taste a Bit
Bubble Sortvar compare = function (x, y) { return x - y; }
var swap = function (a, i, j) { var t = a[x]; a[x] = a[y]; a[y] = t;}
var bubbleSort = function (array) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { if (compare(array[y], array[y + 1]) > 0) { swap(array, y, y + 1); } } }}
Animate it!var compare = function (x, y, callback) { setTimeout(10, function () { callback(x - y); });}
var swap = function (a, i, j, callback) { var t = a[x]; a[x] = a[y]; a[y] = t; repaint(a); setTimeout(20, callback);}
var outerLoop = function (array, x, callback) { if (x < array) { innerLoop(array, x, 0, function () { outerLoop(array, x + 1, callback); }); } else { callback(); }}
var innerLoop = function (array, x, y, callback) { if (y < array.length - x) { compare(array[y], array[y + 1], function (r) { if (r > 0) { swap(array, y, y + 1, function () { innerLoop(array, x, y + 1, callback); }); } else { innerLoop(array, x, y + 1, callback); } }); } else { callback(); }}
outerLoop(array, 0, function () { console.log("done!");});
Animate it!var compare = function (x, y, callback) { setTimeout(10, function () { callback(x - y); });}
var swap = function (a, i, j, callback) { var t = a[x]; a[x] = a[y]; a[y] = t; repaint(a); setTimeout(20, callback);}
var outerLoop = function (array, x, callback) { if (x < array) { innerLoop(array, x, 0, function () { outerLoop(array, x + 1, callback); }); } else { callback(); }}
var innerLoop = function (array, x, y, callback) { if (y < array.length - x) { compare(array[y], array[y + 1], function (r) { if (r > 0) { swap(array, y, y + 1, function () { innerLoop(array, x, y + 1, callback); }); } else { innerLoop(array, x, y + 1, callback); } }); } else { callback(); }}
outerLoop(array, 0, function () { console.log("done!");});What th
e hell is that?
Bubble Sort Animationvar compareAsync = eval(Jscex.compile("async", function (x, y) { $await(Jscex.Async.sleep(10)); // each "compare" takes 10 ms. return x - y;}));
var swapAsync = eval(Jscex.compile("async", function (a, x, y) { var t = a[x]; a[x] = a[y]; a[y] = t; // swap repaint(a); // repaint after each swap $await(Jscex.Async.sleep(20)); // each "swap" takes 20 ms.}));
var bubbleSortAsync = eval(Jscex.compile("async", function (array) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { var r = $await(compareAsync(array[y], array[y + 1])); if (r > 0) $await(swapAsync(array, y, y + 1)); } }}));
Bubble Sort Animationvar compareAsync = eval(Jscex.compile("async", function (x, y) { $await(Jscex.Async.sleep(10)); // each "compare" takes 10 ms. return x - y;}));
var swapAsync = eval(Jscex.compile("async", function (a, x, y) { var t = a[x]; a[x] = a[y]; a[y] = t; // swap repaint(a); // repaint after each swap $await(Jscex.Async.sleep(20)); // each "swap" takes 20 ms.}));
var bubbleSortAsync = eval(Jscex.compile("async", function (array) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { var r = $await(compareAsync(array[y], array[y + 1])); if (r > 0) $await(swapAsync(array, y, y + 1)); } }}));
Design Principle #1
Keep Everything for JavaScript Programmer
• Language features
• Language semantics
• Programming experiences
Language Features• Support almost all JavaScript constructs• Loops: while / for / for...in / do
• Conditions: if / switch
• Error handling: try...catch...finally
• Others: return / break / continue / throw
• Not supported• with block
• break / continue to label
• conditional break in switch block
• Nested functions
Language Semantics
• Keep all semantics as JavaScript
• Explicit “bind” operations (like $await)• The only extension to the language
• Act like a method call
• Make clear which operation is “special”
Programming Experiences
• Write, execute, debug as JavaScript
• Modify and take effects immediately
• No extra compilation process• Code generation by JIT compiler as code runs
Async Programming is Difficult
Code Locality is Broken
• Used to expressing algorithms linearly
• Async requires logical division of algorithms
• Very difficult to• Combine multiple asynchronous operations
• Deal with exceptions and cancellation
Asynchronous Function
// use async builder to execute the compiled codevar somethingAsync = eval(Jscex.compile("async", function (...) { // implementation }));
function () { var res = $await(<async work>);}
React!!!
function () { var res = $await(<async work>);}
React!!!
an HTTP Requestan UI Event
a Timer Callbacka Query Response
a Web Service Responsean Agent Message
function () { var img = $await(readAsync("http://...")); console.log("loaded!"); $await(writeAsync("./files/...")); console.log("saved!");}
=(function () { var _b_ = Jscex.builders["async"]; return _b_.Start(this, _b_.Delay(function () { _b_.Bind(readAsync(...), function (img) { console.log("loaded!"); return _b_.Bind(writeAsync(...), function () { console.log("saved!"); return _b_.Normal(); }); }); }) );})
Work with Expressvar app = express.createServer();
app.get('/', function (req, res) { /** * Question: How to do async work here? e.g.: * * 1. Get multiple keys from database. * 2. For each key, try get data from cache. * If cache missed, get data from database. * 3. Write a list of result to the response. * * Note: all "get" operations are asynchronous. **/});
app.listen(3000);
app.getAsync('/', eval(Jscex.compile("async", function (req, res) { var keys = $await(db.getKeysAsync(...)); var results = []; for (var i = 0; i < keys.length; i++) { var r = $await(cache.getAsync(keys[i])); if (!r) { r = $await(db.getAsync(keys[i])); }
results.push(r); } res.send(generateList(results));})));
I/O Parallelism
• Software is often I/O bound• Leveraging web services
• Working with data on disk
• Network and disk speeds increasing slower
• I/O resources are inherently parallel• Huge opportunity for performance
Make Things Parallelvar getDataAsync = eval(Jscex.compile("async", function (key) { var res = $await(cache.getAsync(key)); if (res) return res; return $await(db.getAsync(key));}));
app.getAsync('/', eval(Jscex.compile("async", function (req, res) { var keys = $await(db.getKeysAsync(...));
// get tasks of “get data” (not started yet) var tasks = keys.map(function (key) { return getDataAsync(key); });
// make requests in parallel var results = $await(Jscex.Async.parallel(tasks)); res.send(generateList(results));})));
Task Model• Async library use a simple task model• Easy to write bindings for async operations
• Parallel: $await(Jscex.Async.parallel(taskA, taskB))
• Series: $await(taskA.continueWith(taskB))
• The semantics of $await: wait a task to complete• Start the task if it’s not running
• Return immediately if it’s already completed
• We can start a task manually (if necessary)• E.g. taskA.start(); $await(taskB); $await(taskA);
AdvancedAsync Patterns
The Limitation of Callback-based Model// if the sequence of processing is important, how to keep it?var i = 1;conn.onAsync("data", eval(Jscex.compile("async", function () { var id = i++;
$await(step1); console.log("step 1 - request " + id); $await(step2); console.log("step 2 - request " + id); /** * A possible sequence (which is unacceptable): * step 1 - request 1 * step 1 - request 2 * step 2 - request 2 * step 2 - request 1 **/})));
Erlang-like Agent
var i = 0;var agent = Agent.start(eval(Jscex.compile("async", function (mailbox) { var id = i++;
var msg = $await(mailbox.receive()); $await(step1); console.log("step 1 - request " + id); $await(step2); console.log("step 2 - request " + id);})));
conn.on("data", function (data) { // data would be queued in mailbox agent.send(data);});
Jscex Components
Language & Libraries
• Compiler (language extension)• JIT: generate code at runtime (development)
• AOT: generate code before runtime (production)
• Builders (libraries)• Async: simplfied async programming
• Sequense: generator for lazy iterator (Py, C#, JS 1.7)
• more...
AOT Compiler// before AOT compilationAgent.start(eval(Jscex.compile("async", function (mailbox) { ...})));
// after AOT compilation// independent of compiler scripts// need a tiny library only (3kb when gzipped)Agent.start((function (mailbox) { var _b_ = Jscex.builders["async"]; return _b_.Start(this, ... );}));
Jscex Builders
• Compiler generates code into standard patterns with a builder (name)• A builder defines its name of the “bind” operation
• Execute with the standard methods implemented in the builder
Not only for Async// infinite fibonacci sequencevar fib = eval(Jscex.compile("seq", function () { var i = 0, j = 1; while (true) { $yield(i); // the bind operation
var t = i; i = j; j += t; }}));
var iter = fib().skip(10).take(10);while (iter.moveNext()) { console.log(iter.current);}
... and Maybe Monadvar maybeFunc = function () { var maybeA = getA(); if (maybeA == Maybe.None) return Maybe.None;
var maybeB = getB(); if (maybeB == Maybe.None) return Maybe.None;
return maybeA.value + maybeB.value;}
// simplified versionvar maybeFunc = eval(Jscex.compile("maybe", function () { var a = $try(getA()); var b = $try(getB()); return a + b;}));
Jscex Internals
Performance
• Function has “linear” and “async” parts• Linear part: compiler keeps linear codes as much as
possible
• Async part: at the same level of hand-writing, callback-based implementation
• An async operation always takes much more time than a bunch of normal codes• Jscex never becomes a performance issue / bottleneck
Code Generation Patterns
• Code generation is direct and simple• Map to original code easily
• Easy to debug: use “debugger” statement to pause or add breakpoints in debuggers
• Based on standard methods• Start, Delay, Combine
• Loop, Try
• Normal, Return, Break, Continue, Throw
• Bind
Debug Browser Scripts
Debug Node.js Scripts
Try it NOW
• Release under BSD license
• https://github.com/JeffreyZhao/jscex
Q & A
Thanks
Recommended