深入浅出jscex

59
深入浅出Jscex 赵劼 - 2011.10

Upload: jeffz

Post on 10-May-2015

3.682 views

Category:

Technology


25 download

TRANSCRIPT

Page 1: 深入浅出Jscex

深入浅出Jscex赵劼 - 2011.10

Page 2: 深入浅出Jscex

关于我• 赵劼 / 老赵 / Jeffrey Zhao / 赵姐夫

• 日写代码三百行,不辞长作程序员• 博客:http://blog.zhaojie.me/

• 微博:@老赵

• F#, JavaScript, Scala, C#, Python, .NET, Mono...

• 痛恨Java语言

Page 3: 深入浅出Jscex

Jscex是什么?

• JavaScript Computation EXpression

• JavaScript语言扩展,用于简化某些常见场景下的编程

• F#计算表达式特性的JavaScript移植• 受“计算表达式”特性启发• 为JavaScript设计,基于JavaScript实现

Page 4: 深入浅出Jscex

Jscex不是什么?

• 另一种语言• Jscex是百分百的JavaScript

• 框架• Jscex是类库,能够与几乎任何类库/框架一起使用

• JavaScript引擎/运行时• 在任何ECMAScript 3上执行

Page 5: 深入浅出Jscex

浅尝辄止

Page 6: 深入浅出Jscex

冒泡排序var compare = function (x, y) { return x - y; }

var swap = function (a, i, j) { var t = a[i]; a[i] = a[j]; a[j] = 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); } } }}

Page 7: 深入浅出Jscex

做成动画var compare = function (x, y, callback) { setTimeout(10, function () { callback(x - y); });}

var swap = function (a, i, j, callback) { var t = a[i]; a[i] = a[j]; a[j] = 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!");});

Page 8: 深入浅出Jscex

做成动画var compare = function (x, y, callback) { setTimeout(10, function () { callback(x - y); });}

var swap = function (a, i, j, callback) { var t = a[i]; a[i] = a[j]; a[j] = 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!");});

这TMD是什么啊!

Page 9: 深入浅出Jscex

冒泡排序动画var 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, i, j) { var t = a[i]; a[i] = a[j]; a[j] = 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)); } }}));

http://files.zhaojie.me/jscex/samples/async/sorting-animations.html?bubble

Page 10: 深入浅出Jscex

冒泡排序动画var 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, i, j) { var t = a[i]; a[i] = a[j]; a[j] = 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)); } }}));

Page 11: 深入浅出Jscex

异步编程十分困难

• 破坏代码局部性• 程序员习惯线性地表达算法• 异步代码将逻辑拆分地支离破碎

• 难以• 异步操作之间的协作及组合• 处理异常及取消

Page 12: 深入浅出Jscex

Jscex异步函数

// 使用异步构造器执行编译后的代码var somethingAsync = eval(Jscex.compile("async", function (...) { // 实现 }));

Page 13: 深入浅出Jscex

function () { var res = $await(<async work>);}

响应

Page 14: 深入浅出Jscex

function () { var res = $await(<async work>);}

响应

HTTP请求UI事件时钟回调查询响应

Web Service响应代理消息

Page 15: 深入浅出Jscex

代码编译(转换)

var f = eval(Jscex.compile("async", function () { var img = $await(readAsync("http://...")); console.log("loaded!"); $await(writeAsync("./files/...")); console.log("saved!");}));

Page 16: 深入浅出Jscex

var f = eval('(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(); }); }); }) );})');

等价于…

Page 17: 深入浅出Jscex

var f = (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(); }); }); }) );});

等价于…

Page 18: 深入浅出Jscex

使用Express框架var app = express.createServer();

app.get('/', function (req, res) { /** * 问题:如何进行异步操作?例如: * * 1. 从数据库中获取多个键值。 * 2. 对于每个键,从缓存中获取数据。 * 如果缓存失效,则从数据库里获取数据。 * 3. 将结果列表发送至res中。 * * 注意:所有的“获取”操作都是异步的。 **/});

app.listen(3000);

Page 19: 深入浅出Jscex

基于Jscex编程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.getItemAsync(keys[i])); }

results.push(r); } res.send(generateList(results));})));

Page 20: 深入浅出Jscex

I/O并行

• 许多程序是I/O密集型应用• 使用Web服务• 使用磁盘上的数据

• 网络和磁盘速度发展很慢• I/O资源天然可以并行• 提高性能的关键方式

Page 21: 深入浅出Jscex

并行执行var getItemAsync = eval(Jscex.compile("async", function (key) { var res = $await(cache.getAsync(key)); if (res) return res; return $await(db.getItemAsync(key));}));

app.getAsync('/', eval(Jscex.compile("async", function (req, res) { var keys = $await(db.getKeysAsync(...));

// 获取“请求数据”的任务(尚未执行) var tasks = keys.map(function (key) { return getItemAsync(key); }); // 同时发起所有异步任务 var results = $await(Jscex.Async.parallel(tasks)); res.send(generateList(results));})));

Page 22: 深入浅出Jscex

任务模型• 异步类库使用一种十分简单的任务模型• 可以轻松地将各种异步操作融入这种模型• 并行:$await(Jscex.Async.parallel(taskA, taskB))

• 串行:$await(taskA.continueWith(taskB))

• $await语义:等待任务完成• 如果任务尚未执行,则启动• 如果任务已经结束,则立即返回

• 在需要的时候可以手动启动任务• 例如:taskA.start(); $await(taskB); $await(taskA);

Page 23: 深入浅出Jscex

异步回调模型的限制// 如果处理顺序十分重要,那么如何保持这些顺序?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); /** * 一种可能的顺序(但不可接受) * step 1 - request 1 * step 1 - request 2 * step 2 - request 2 * step 2 - request 1 **/})));

Page 24: 深入浅出Jscex

... the principle we go by is, don't expect to see a particular concurrency model put into C# because there're many different concurrency model ... it's more about finding things are common to all kinds of concurrency ...

- Anders Hejlsberg

Page 25: 深入浅出Jscex

Erlang形式的代理var i = 0;var agent = Agent.start(eval(Jscex.compile("async", function (mailbox) { while (true) { 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) { // 数据会缓存在mailbox队列中 agent.send(data);});

Page 26: 深入浅出Jscex

Jscex设计原则

• 为JavaScript程序员保留一切• 语言特性• 语言语义• 编程体验

• 清晰区分“特别”之处

Page 27: 深入浅出Jscex

语言特性• 支持几乎所有JavaScript语言功能• 循环:while / for / for...in / do

• 判断:if / switch

• 错误处理:try...catch...finally

• 其他:return / break / continue / throw

• 不支持的语言特性• with块• 带标签的break和continue

• switch内带条件的break

• 保留上下文执行环境

Page 28: 深入浅出Jscex

保留上下文执行环境

// 使用异步构造器执行编译后的代码var somethingAsync = eval(Jscex.compile("async", function (...) { // 实现 }));

Page 29: 深入浅出Jscex

eval使用当前上下文Sample = { // 返回函数的代码 compile: function (func) { return "(" + func.toString() + ")"; },

// 返回eval后的结果 compileEx: function (func) { return eval("(" + func.toString() + ")"); }} 上下文为当前代码执行处

Page 30: 深入浅出Jscex

不可封装的eval(function () { var a = 1;

// f1使用当前上下文 var f1 = eval(Sample.compile(function () { alert(a); })); f1(); // 显示“1”

// f2的上下文为Sample.compileEx内部 var f2 = Sample.compileEx(function () { alert(a); }); f2(); // 引发“a未定义”错误})();

Page 31: 深入浅出Jscex

语言语义

• 保持完整的JavaScript语义

• 独立的“bind”操作(例如$await)• 语言里的唯一扩展• 表现形式为“方法调用”

• 清楚表明“特殊”的操作

Page 32: 深入浅出Jscex

编程体验

• 如JavaScript一般编写、执行、调试

• 修改后立即生效• 没有额外的编译步骤• 代码执行过程中由JIT编译器生成代码• 与传统JavaScript文件一样直接加载

Page 33: 深入浅出Jscex

清晰区分“特别”之处MyClass.prototype = { get: function (n) { ... }, calculateAsync: eval(Jscex.compile("async", function (n) { var numbers = []; for (var i = 0; i < n; i++) numbers.push(i); this.result = $await(make(numbers)); }))}

Page 34: 深入浅出Jscex

清晰区分“特别”之处MyClass.prototype = { get: function (n) { ... }, calculateAsync: eval(Jscex.compile("async", function (n) { var numbers = []; for (var i = 0; i < n; i++) numbers.push(i); this.result = $await(make(numbers)); }))}

普通函数

Jscex函数

Jscex绑定操作

Page 35: 深入浅出Jscex

Jscex vs. 其他方案• 许多项目都在设法简化异步编程难度• 异步类库• Virtual Panel: How to Survive Asynchronous

Programming in JavaScript

• 异步语言• StratifiedJS

• Narrative JavaScript

• Streamline(可能是与Jscex最相似的项目)

Page 36: 深入浅出Jscex

Jscex vs. 异步类库• 异步类库• 在类库建立的规则内编写代码• 有限的灵活程度与表达能力• 需要较多学习

• Jscex• 使用JavaScript表达逻辑• 极高的灵活度和表达能力• 几乎无需学习

Page 37: 深入浅出Jscex

Jscex vs. 异步语言• 异步语言• 学习新语法• 学习新语义• 改变传统编程体验

• Jscex• 完全使用JavaScript语法• 完全保留JavaScript语义• 完全保留JavaScript编程体验

Page 38: 深入浅出Jscex

Streamline

• 可能是与Jscex最相似的项目• 保留JavaScript语法、语义• 简单修改即可JIT编译• 无需独立的异步操作语句

• 与Jscex相较最大的区别• 目标代码生成方式• Yield – Resume vs. Asynchronous Callbacks – An

Equivalence

Page 39: 深入浅出Jscex

Streamline (input)

var bubbleSortAsync = function (array, _) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { if (compareAsync(array[y], array[y + 1], _) > 0) swapAsync(array, y, y + 1, _); } }}

Page 40: 深入浅出Jscex

Streamline (compiled)

http://sage.github.com/streamlinejs/examples/streamlineMe.html

var bubbleSortAsync = function __1(array, _) { if (!_) { return __future(__1, arguments, 1); }; var __then = _; var x = 0; var __4 = false; return function(__break) { var __loop = __nt(_, function() { var __then = __loop; if (__4) { x++; } else { __4 = true; } ; if ((x < array.length)) { var y = 0; var __3 = false; return function(__break) { var __loop = __nt(_, function() { var __then = __loop; if (__3) { y++; } else { __3 = true; } ;

if ((y < (array.length - x))) { return compareAsync(array[y], array[(y + 1)], __cb(_, function(__0, r) { if ((r > 0)) { return swapAsync(array, y, (y + 1), __cb(_, __then)); } ; return __then(); })); } else { return __break(); } ; }); return __loop(); }(__then); } else { return __break(); } ; }); return __loop(); }(__then);};

Page 41: 深入浅出Jscex

Jecex (input)

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)); } }}));

Page 42: 深入浅出Jscex

Jecex (compiled)var bubbleSortAsync = (function (array) { var _b_ = Jscex.builders["async"]; return _b_.Start(this, _b_.Delay(function () { var x = 0; return _b_.Loop( function () { return x < array.length; }, function () { x++; }, _b_.Delay(function () { var y = 0; return _b_.Loop( function () { return y < (array.length - x); }, function () { y++; } _b_.Delay(function () { return _b_.Bind(compareAsync(array[y], array[y + 1]), function (r) { if (r > 0) { return _b_.Bind(swapAsync(array, y, y + 1), function () { return _b_.Normal(); }); } else { return _b_.Normal(); } }); }), false ); }), false ); }) );})

Page 43: 深入浅出Jscex

Jecex (compiled)var bubbleSortAsync = (function (array) { var _b_ = Jscex.builders["async"]; return _b_.Start(this, _b_.Delay(function () { var x = 0; return _b_.Loop( function () { return x < array.length; }, function () { x++; }, _b_.Delay(function () { var y = 0; return _b_.Loop( function () { return y < (array.length - x); }, function () { y++; } _b_.Delay(function () { return _b_.Bind(compareAsync(array[y], array[y + 1]), function (r) { if (r > 0) { return _b_.Bind(swapAsync(array, y, y + 1), function () { return _b_.Normal(); }); } else { return _b_.Normal(); } }); }), false ); }), false ); }) );})

outer loop

inner loop

$await(compareAsync(...))$await(swapAsync(...))

Page 44: 深入浅出Jscex

Jscex代码生成模式• 代码生成模式简单而直接• 能够轻松映射至原有代码• 易于调试:使用“debugger”语句暂停代码,或在调试器中设置断点

• 为JavaScript设计的标准方法• Start, Delay, Combine

• Loop, Try

• Normal, Return, Break, Continue, Throw

• Bind

Page 45: 深入浅出Jscex

调试浏览器脚本

Page 46: 深入浅出Jscex

调试Node.js脚本

Page 47: 深入浅出Jscex

语言与类库• 编译器(语言扩展)• JIT:在运行时生成代码(用于开发环境)• AOT:在运行之前生成代码(用于生成环境)

• 构造器(类库)• 异步构造器:简化异步编程• 序列构造器:延迟加载的迭代器(如Python,C#,

JavaScript 1.7等等)• 更多…

Page 48: 深入浅出Jscex

AOT编译器// AOT编译前Agent.start(eval(Jscex.compile("async", function (mailbox) { ...})));

// AOT编译后// 可以独立于编译器脚本执行// 只需极小的核心类库(gzip后大约3kb)Agent.start((function (mailbox) { var _b_ = Jscex.builders["async"]; return _b_.Start(this, ... );}));

Page 49: 深入浅出Jscex

Jscex构造器

• 编译器根据指定的构造器(名称)将代码编译为标准模式• 构造器指定自己需要的“绑定”操作名称

• 由定义在构造器上的标准方法执行代码

Page 50: 深入浅出Jscex

不仅仅是异步编程// 无限的菲波纳契数列var 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);}

Page 51: 深入浅出Jscex

…还有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;}

// 简化后的版本var maybeFunc = eval(Jscex.compile("maybe", function () { var a = $try(getA()); var b = $try(getB()); return a + b;}));

Page 52: 深入浅出Jscex

性能

• 异步函数包含“线性部分”及“异步部分”• 线性部分:编译器尽可能保留原代码的线性部分• 异步部分:与基于回调函数的手动实现相近

• 单个异步操作耗时远胜大段线性代码• Jscex不会成为程序的性能问题 / 瓶颈

Page 53: 深入浅出Jscex

for编译为Loopfunction (urls) { for (var i = 0; i < urls.length; i++) { $await(requestAsync(urls[i])); }}

Page 54: 深入浅出Jscex

for编译为Loopfunction (urls) { for (var i = 0; i < urls.length; i++) { $await(requestAsync(urls[i])); }} function (urls) {

var _b_ = Jscex.builders["async"]; return _b_.Start(this, _b_.Delay(function () { var i = 0; return _b_.Loop( function () { return i < urls.length; }, function () { i++; }, _b_.Delay(function () { return _b_.Bind(requestAsync(urls[i]), function () { return _b_.Normal(); }); }), false ); }) );}

Page 55: 深入浅出Jscex

for完全保留function (urls) { var requests = [];

for (var i = 0; i < urls.length; i++) { requests.push(requestAsync(urls[i])); }

$await(Jscex.Async.parallel(requests));}

Page 56: 深入浅出Jscex

for完全保留function (urls) { var requests = [];

for (var i = 0; i < urls.length; i++) { requests.push(requestAsync(urls[i])); }

$await(Jscex.Async.parallel(requests));}

function (urls) { var _b_ = Jscex.builders["async"]; return _b_.Start(this, _b_.Delay(function () {

var requests = []; for (var i = 0; i < urls.length; i++) { requests.push(requestAsync(urls[i])); }

return _b_.Bind(Jscex.Async.parallel(requests), function () { return _b_.Normal(); }); }) );}

保留同步代码块

Page 57: 深入浅出Jscex

立即尝试

• 基于BSD协议发布

• 站点• 英文:https://github.com/JeffreyZhao/jscex

• 中文:http://www.sndacode.com/projects/jscex

Page 58: 深入浅出Jscex

Q & A

Page 59: 深入浅出Jscex

谢谢