抱歉呀,今天才仔细阅读了一遍 Jscex 的文档,按照阅读顺序,提供一些建议:
首页
jscex.info
这域名,是因为 jscex.org
被人抢占了么?如果 org 在老赵手上,建议用 org,这是开源组织的标配。另外,建议首页默认是中文,右上角给个英文版的链接就好。毕竟 Jscex 目前主要是想在大陆推广吧?国际化可以缓一缓。我们可以先尝试下先国内后国外的战略,发展**家包围发达国家。能成的话,也是一种新气象,不然国人老是以为国外的月亮更圆,我们得让同胞们看看我们自身的实力,扭转大众的一些误区。
- Jscex 专项拨款挺好的,但缺乏明确的游戏规则。建议可以将参与方式、奖品规则写一篇博客详细介绍下,这样能更让诚意更具诚意,说不定哪位心头一热,就会开始给社区做贡献了。虽然奖品不是目的,但明确的游戏规则可以让运营更实际有效。
- “能够在任意支持ECMAScript 3的执行引擎里使用”,这句话建议去掉,容易给普通用户造成误解,比如:什么是 ECMAScript 3?怎么只支持 ECMAScript 3,难道不支持 ECMAScript 5 吗?可以考虑改成:能够在任意主流 JavaScript 引擎中使用。至于什么是主流 JS 引擎,知者自知,不必去解释,除非真有人问题(一般不会有人问)。
- 冒泡排序,对于计算机科班出身的程序员来说,不是问题。但 Jscex 如果想推广给更多前端程序员用,则写一小段文字或给出参考链接来解释下什么是冒泡排序还是有必要的,比如 NCZ 写过一篇 Computer science in JavaScript: Bubble sort。把用户当白痴,很重要很重要。国内技术圈,比我们想象中的浮躁很多。
- 冒泡排序这个例子,对于 Jscex 来说,核心还是动画的实现。我建议直接换成一个简单的 “Hello, World” 的文字飘入动画就好。今晚有时间的话,我来基于 seajs 的 plugin-jscex 做一个。这样应该能更亲民一些。冒泡排序可以作为进阶中的案例。
- “增加交换和比较操作的耗时,因为排序算法的性能主要取决于交换和比较操作的次数多少。” 这个说得不清楚,在这个例子里,增加耗时的目的,我觉得跟性能无关,而是为了让动画看起来不那么太快。建议改成:“增加交换和比较操作的耗时,这样在动画演示时能比较容易看清楚。”
eval(Jscex.compile("async", function (x, y) {
这种写法,对入门者来说还是比较逆天的。打算在 seajs/plugin-jscex 里,把这技术实现细节隐藏掉。老赵等我的插件。
$await(Jscex.Async.sleep(10))
的写法,感觉也不够简洁。感觉也是将内部的实现暴露了出来。建议可以为外部用户提供一个 shortcut: $await(10)
. 其实我还有一个疑问:为什么要用 await ? 而不是更直白的 wait 或 sleep ?
开发指南
require("jscex-parser").init()
建议简化成 require("jscex-parser")
,init 内置掉。我还是期望对使用者来说,不要去理解什么是核心组件,什么是 parser,只要简简单单 var Jscex = require(jscex)
就万事大吉了。jit 可以让用户按需引入。或者变成:
require('jscex') // 用于开发时,含 core、buildbase, async, parser 和 jit
require('jscex-runtime') // 用户上线后,只含 core, buildbase, async
这样也方便用户部署,而且大部分用户,用开发时的就满足了需求,runtime 版本交给那些对性能有追求的高级使用者去玩。
- 构造器基础模块究竟是干嘛的?文档里没有解释什么是构造器,以及什么时候需要自己构建构造器?看得不是很明白。这应该是高级用户才会去玩的吧?估计要看源码才知道如何去写。在文档里,如果某个内容说不清楚,建议不如先隐藏掉。
- 包引入这文档,动态依赖和静态依赖貌似写反了。静态依赖是需要手动自己去写依赖,动态依赖是指可以动态自动加载。还是我理解错了?
- async 居然还有一个 powerpack, 建议如果不是真的 power 到几十K的体积,建议就放到默认的 async 里。
- 感觉太多内部细节暴露给了使用者,感觉不是很妥当。是否可以在现有 API 的基础上,再包装出一层更傻瓜的高级 API ?比如上面提到了一些:
require('jscex') // <-- 80% 的用户,只要这一句就好了,然后全局就有了 Jscex 和 $await 等变量
$await(10) // <-- 等价 $await(Jscex.Async.sleep(10))
异步模块
这个文档入口,在开发指南里居然没有。我花了好长时间才从首页找到一个入口,有点囧。
$await指令的使用形式便是普通的方法调用,但事实上在上下文中并没有这个方法。它的作用与eval(Jscex.compile("async", …))一样,仅仅是个占位符,让Jscex知道在这个地方需要进行“特殊处理”。
这段话是否有误?能理解 $await
是个占位符,eval(Jscex.compile("async", …))
怎么也成了占位符?我没看 jscex 的源码,直觉上,Jscex.compile("async", fn)
是一个真实的普通函数,这个函数执行时,会分析第二个参数 fn 中的 $await
等占位符,然后进行一系列操作,结果是一段编译后的代码,最后交给 eval 去在当前 context 上执行。我理解是否有误?
这个并发的例子:
var queryUserTask = queryUserAsync(userId);
// 手动启动queryUserAsync任务,start方法调用将立即返回。
queryUserTask.start();
var items = $await(queryItemsAsync(userId));
var user = $await(queryUserTask); // 等待之前的任务完成
感觉理解起来还是有点绕。不如 async 等类库提供的 api 方便,比如类似:
async. parallel([queryUserAsync(userId), queryItems(userId)], function(users, items) {
// do sth
})
async 类库不反对异步回调,只是将回调套回调的写法变得更简单清晰。文档读到这里,Jscex 对我来说,貌似只有 $await
让我觉得非常诱人,难以舍弃-.-
任务模型
Event 这个例子让人有点小兴奋了,居然还可以这样用。我前面有些地方误解 Jscex 了,误解的文字也就不去修改了,保留一份阅读文档过程中的真实记录。
我喜欢这个:
return $await(Task.whenAll({
user: queryUserAsync(userId),
items: queryItemsAsync(userId)
}));
比 async 类库强太多了。依旧期待是否在包装出一个高级 API,比如:
return $await.parallel({
user: queryUserAsync(userId),
items: queryItemsAsync(userId)
}));
CancellationToken
又是高级用户内容了,这被普通用户看见,会有心理障碍的-.-
这取消模型还是得赞下,设计得挺棒的。
下面的貌似都是高级内容,我决定需要的时候再看,这份建议就写到这。
最后说个总体感觉,$await
魅力无穷,但需要通过 eval(Jscex.compile...)
或 Task.create
来创建 AsyncTask 给 $await 调用,感觉对普通用户有难度,需要理解的东西比较多,写的时候要换一些思维去想。
$await
是更自然的异步流程控制,但感觉 task 不是很自然,没有传统的 callback 容易理解。
是否可以将 Task 隐藏掉?比如首页的例子如果能像下面这样写就帅呆啦:
function compare(x, y) {
$await(10); // 暂停10毫秒
return x - y;
}
function swap(a, i, j) {
$await(20); // 暂停20毫秒
var t = a[i]; a[i] = a[j]; a[j] = t;
paint(a); // 重绘数组
}
function bubbleSortAsync(array) {
for (var x = 0; x < array.length; x++) {
for (var y = 0; y < array.length - x; y++) {
// 异步比较元素
var r = $await(compare(array[y], array[y + 1])); // 如果这里也可以把 $await 省略掉就更帅啦
// 异步交换元素
if (r > 0) $await(swap(array, y, y + 1));
}
}
}
上面的代码,是在某一个文件里,比如 xx.js,这在 node 或 commonjs 里,都是一个模块,意味着我们可以在模块编译时,做点手脚,比如对于 NodeJS 环境,可以在 Module._compile 调用时,自动将 eval(Jscex.compile...)
加上。
SeaJS 的 plugin-jscex 我就是想这么干的,让开发时用户可以直接写成:
define(function(require, exports, module) {
function compare(x, y) {
$await(10); // 暂停10毫秒
return x - y;
}
function swap(a, i, j) {
$await(20); // 暂停20毫秒
var t = a[i]; a[i] = a[j]; a[j] = t;
paint(a); // 重绘数组
}
function bubbleSortAsync(array) {
for (var x = 0; x < array.length; x++) {
for (var y = 0; y < array.length - x; y++) {
// 异步比较元素
var r = $await(compare(array[y], array[y + 1])); // 如果这里也可以把 $await 省略掉就更帅啦
// 异步交换元素
if (r > 0) $await(swap(array, y, y + 1));
}
}
}
})
然后我在模块编译时,通过 plugin-jscex, 将上面的代价等价成:
define(eval(Jscex.compile('async', function(require, exports, module) {
function compare(x, y) {
$await(10); // 暂停10毫秒
return x - y;
}
function swap(a, i, j) {
$await(20); // 暂停20毫秒
var t = a[i]; a[i] = a[j]; a[j] = t;
paint(a); // 重绘数组
}
function bubbleSortAsync(array) {
for (var x = 0; x < array.length; x++) {
for (var y = 0; y < array.length - x; y++) {
// 异步比较元素
var r = $await(compare(array[y], array[y + 1])); // 如果这里也可以把 $await 省略掉就更帅啦
// 异步交换元素
if (r > 0) $await(swap(array, y, y + 1));
}
}
}
})))
老赵看看这样是否可行?等你确定后我再来折腾 plugin-jscex. 如果可行就太好了。