Hello World
Spiga

受禁锢的异步编程思维

2011-12-19 21:49 by 老赵, 8094 visits

最近一直在努力推广Jscex,补充了很多中文文档和示例,因此博客上都已经有两篇文章有了“上”而没有“下”,即使最复杂的图示也已经绘制完毕。在推广Jscex的过程中,我发现有个比较明显的问题是,许多使用JavaScript的程序员已经习惯旧有的编程方式,甚至推崇一些据他们说很“漂亮”的模式。但在我看来,这其实跟许多GoF模式是在修补OO语言的不足有些类似,很多异步模式都只是因为JavaScript语言特性不足而设计出来的“权宜之计”。我们在传统JavaScript编程环境下并没有其他选择,单纯地认为这是“美”,说实话只不过是一种安慰罢了。

Jscex的重头戏便是处理异步操作,但异步操作并不只是如Node.js中通过回调函数传回结果的那些方法,或者是网页上的AJAX请求等等。异步操作的定义其实可以概括成“会在未来某个时刻完成的操作”,就只是这么简单。什么事情会在未来发生,那么它对你来说就是个异步操作。因此其实在日常开发过程中可谓到处是异步操作,例如:

  • 播放动画(播放会在未来结束)
  • 模态对话框关闭(模态对话框会在未来关闭)
  • 用户操作(用户会在未来点击按钮)
  • 各类事件(数据流会在未来关闭,WebWorker会在未来获得消息,图片会在未来加载成功)

这些示例实在数不胜数。但是,在许多JavaScript程序员眼中,似乎只有AJAX或是Node.js中的那些异步方法才算是异步操作。其他的东西,比如用户点击一个按钮,这难道不是个天然的“事件”吗?其实这就要视这个异步任务的性质如何了。如果它是一系列操作的“发起者”,那么的确,使用事件触发的方式来对待这次点击操作可能是最合理的。但如果,这个操作只是一系列过程中的一个步骤,那么如果依然把它视为一个事件型的操作,就只会破坏我们的逻辑了。

举个例子,和Jscex的快速入门比较类似,即菲薄纳契(Fibonacci)数列

其边界情况为:

以上是其标准定义,直接写成算法即是:

var fib = function () {

    console.log(0);
    console.log(1);

    var a = 0, current = 1;
    while (true) {
        var b = a;
        a = current;
        current = a + b;

        console.log(current);
    }
};

上述代码将会无限地循环下去,不断输出数列的每一项。快速入门里的要求,是将其修改为“每隔一秒输出一个数字”,于是有同学就说:这不天生是计时器的场景吗?但事实并非如此。“计时器”或是setTimeout函数,都只是环境提供给我们的唯一可用的功能,我们要意识到这不是我们主动的“选择”。如果一看到“每隔一秒”这样的需求,JavaScript程序员就认为“计时器”是“最好”的办法,这就说明思维被禁锢了。我相信这样的功能交给其他任何平台的程序员,他们的第一感觉几乎都会是“使用Sleep函数暂停一秒”。这其实才是最简单的做法,直接,清晰,完整保留现有代码逻辑。

这也是基于Jscex之后的实现方式。这里我再将要求修改一下,改为用户“每点击一次按钮”输出一个数字,又该怎么做?基于Jscex的做法如下:

var Async = Jscex.Async;

var fibAsync = eval(Jscex.compile("async", function () {

    var button = document.getElementById("button");

    $await(Async.onEvent(button, "click")); // 等待用户点击
    console.log(0);

    $await(Async.onEvent(button, "click")); // 等待用户点击
    console.log(1);

    var a = 0, current = 1;
    while (true) {
        var b = a;
        a = current;
        current = a + b;

        $await(Async.onEvent(button, "click")); // 等待用户点击
        console.log(current);
    }
}));

fibAsync().start();

有朋友可能会问:用户点击按钮不是需要响应事件的嘛,这个事件到哪里去了?其实正像我所说的那样,把这里的“用户点击按钮”当作事件对待并非最合理的方式,因为它只是“整个过程”中的一个环节而已。在这里,我们其实只是要在输出数字之前“等待用户点击”即可,这个“输出”以及相关的“计算”操作,并非是由“按钮点击”所触发的逻辑,而是一个连续的统一过程中的一部分而已。

您可以试试纯粹使用事件机制来实现这个功能,保证您需要重新实现这段斐波那契数列的逻辑。当然,菲薄纳契数列的逻辑很简单,重写下估计也不会花太大的功夫,但如果您需要改造汉诺塔的动画效果呢?

var hanoiAsync = eval(Jscex.compile("async", function (n, from, to, mid) {
    if (n > 0) {
        $await(hanoiAsync(n - 1, from, mid, to));
    }

    // 等待按钮点击
    // var btnNext = document.getElementById("btnNext");
    // $await(Jscex.Async.onEvent(btnNext, "click"));

    $await(moveDishAsync(n, from, to));

    if (n > 0) {
        $await(hanoiAsync(n - 1, mid, to, from));
    }
}));

以上代码是以动画形式表现汉诺塔的解题过程,但如果用户提出想要“每点一次按钮”才移动一个盘子,那其实我们只要将上面两行代码取消注释即可。如果忽然有一天,老板要求通过一个选项来决定是否“自动移动”,在Jscex里只要加一个if判断即可。您可以简单设想一下直接裸写这些代码会遇到什么样的景象,改造时会遇到哪些困难。

我还为Jscex准备了一个示例,是关于“模态对话框”配合相关异步操作的。由于是“模态对话框”,我们是要在对话框关闭之后才继续做某些事情。可惜在JavaScript中,如果您直接把一个界面元素展现为一个模态对话框,它是无法阻止后面的代码继续执行的,要阻止则只能使用confirm或alert方法。于是,我们只能把后续操作放到一个回调函数中去,并在模态对话框关闭之后才执行。但是您要知道,模态对话框只不过是整个过程中的一个步骤,理想状况下我们的完整功能不该被拆成多个部分,再使用所谓“美妙”的回调串联起来。

这点在Jscex中还是那么简单,直接按最简单的逻辑来进行即可:

// 显示模态对话框
$await($("#dialog-confirm").dialogAsync({ modal: true }));

// 发起AJAX请求
var response = $await($.ajaxAsync({ url: "...", dataType: "text" }));

// 继续做事

而无需:

// 继续做事
$("#dialog-confirm").dialog({
    modal: true,
    close: function () {
        // 发起AJAX请求
        $.ajax({
            url: "...",
            dataType: "text",
            success: function () {
                // 继续做事
            }
        });
});

经常会听到有些朋友谈起,说在实际开发过程中很少遇到异步场景。但在我看来,实在可谓遍地是异步,这种观念的差别只是在于是否经过了“抽象”。不加抽象地使用技术平台为我们提供的异步操作,会让我们的思维被它所禁锢。在JavaScript编程中浸淫太久了,可能就会忘记我们从最初是如何编程的。Jscex的目标,便是将这些东西回归自然,将逻辑以最自然的方式表达出来。循环?那就用for或是while吧,在函数之间跳来跳去是做什么的?

我从来不担心的Jscex的实用价值。Jscex来自C#,F#以及Scala等现成的理念,各种开发模式都是被翻来覆去讨论过,总结过,验证过的。这些语言其实都能实现与JavaScript类似的编程模式,但它们不需要,因为语言特性让程序员可以使用更简单直接的做法来解决问题。Jscex只是将这些现成的内容,从其他模式带到JavaScript编程领域上而已。

如今我唯一担心的,只是那些被禁锢的编程思维。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

78 条回复

  1. ng
    116.232.177.*
    链接

    ng 2011-12-19 22:46:03

    老赵你的about要改改了: 目前就职于盛大创新院产品开发部,研究员。

  2. 老赵
    admin
    链接

    老赵 2011-12-19 23:00:09

    @ng

    无数人提过了,终于下决心改了罢,呵呵。

  3. Wuvist
    175.156.203.*
    链接

    Wuvist 2011-12-20 00:14:37

    这篇写得不错~得顶~

    但,从产出结果来讲,很多时候,程序员用自身已经习惯的开发思维方式去写代码,实际上会更加高效。用coroutine的方式,对于别的语言程序员来讲,也许更加“自然”。但这种“自然”,未必就是别人的自然。

    反过来说,只能以coroutine方式写代码的编程思维,也是受禁锢的。都只是选择而已,胡萝卜青菜,各有所爱。

    我认为在业务逻辑并不复杂的情况下,强调必须使用某种方式才更加“自然”,是没有意义的。

    具体拿jscex那个“模态对话框”来说,jscex的代码实际上要比非jscex代码更长;而且还有编译等额外的麻烦。也就是说,在实际操作上“模态对话框”这个例子并不能说明jscex能够带来优势;相反,用jscex方式去写,还会耗费现在js程序员更多的时间;很少有人熟悉jscex对吧?

    至于“如果忽然有一天,老板要求”这仅仅是一种假设,为可能存在的需求投入时间去开发,是种“premature optimization”。这可同意?

    如果能拿出已经复杂到无法用“传统异步”代码去维护的js项目,然后用jscex去改写,代码量变少,并且逻辑清晰可维护性强;那么会比“模态对话框”有说服力得多。

  4. 风峰枫
    115.204.105.*
    链接

    风峰枫 2011-12-20 00:15:38

    看来我被禁锢的不轻。。。。。

  5. 老赵
    admin
    链接

    老赵 2011-12-20 00:37:18

    @Wuvist

    唉,我从来没说“只能以coroutine方式编程”。我文章里也写了“如果XX,那么YY也是合理的,但如果是ZZ的情况又如何如何”。Jscex是给了另外一种选择,而且Jscex的异步方法生成的Task对象就能以普通方式使用,一直是自由的。

    作为简单的示例逻辑不复杂,但实际情况下会复杂很多。例如我一直觉得不考虑错误处理是不合理的,现在很多看上去简单的异步示例,好多都不考虑错误处理情况了,所以看上去会简单。Jscex里处理异常十分方便,而普通的做法就到处都要处理了。

    模态对话框的例子,Jscex代码似乎没有减少代码(但其实这里同样已经隐含了错误处理),但更重要的其实是清晰,此外编译的麻烦又不是用户需要关注的。“老板要求”可能是特例,可以说是我的假设,但是需求变化是一定会存在的,这个人人都经历过。在我看来,Jscex的易写易改是毫无疑问的,这种“自然”对大部分人来说并不是相对的,因为程序员在接受编程训练的时候,一直是顺序式的逻辑,我不相信有人在学习编程时就开始考虑回调的跳来跳去。如果回调是自然的方式,这个问题也不会一直困扰一代代程序员了,其实大家都在想办法解决这方面问题。“自然”是绝对的,不同的只是思维惯性。

    不过你要说耗费学习时间,我就无言以对了,但这个理由是任何新生事物都会遇到的也没办法。在我看来Jscex的入门门槛已经极低了,因为它就是普通的JavaScript编程。在谈论编程思维的时候,我不太会考虑“习惯”问题,就如比较Ruby和Python的开发效率,要两边的高手才能比,不能说让Ruby程序员去比较Ruby和Python的开发效率。或者可能应该这么说:这里我就是要想方设法让人改变旧有的惯性思维,最终让生产力提升一个台阶。

    改变惯性思维的确有挑战,但就我的经验来说十分值得。所以如果是讨论惯性思维影响有多深,改变用户习惯有多难,都没什么问题,但如果还在怀疑Jscex这种方式对生产力是否有提高,我觉得就没必要纠结这点了,这些已经被论证过很多次了──不如想如何可以多写几个示例。

    当然,各种简单到复杂的例子都是会有的(估计“复制完整文件夹”便是),现在文章里只能演示一些概念,只能以片段进行,所以看上去差距不是很明显。

  6. 老赵
    admin
    链接

    老赵 2011-12-20 00:47:17

    @风峰枫

    一个语言用久了的确容易惯性思维,所以我会去关注任何技术平台,然后设法借鉴点东西过来,这样效率会有很大提高。

  7. Joe
    219.128.255.*
    链接

    Joe 2011-12-20 08:27:19

    逃脱不了浏览器下JS单线程的束缚。

  8. 老赵
    admin
    链接

    老赵 2011-12-20 08:57:07

    @Joe

    是的,现在只是改进编程模型,不可能为浏览器添加线程的。如果要并行之类的功能,只能等WebWorker出来了。

  9. 链接

    张志敏 2011-12-20 08:59:48

    菲薄数列的例子非常好, 这种回调嵌套且前后依赖的场景对 Jscex 来说, 非常合适。 在实际的开发中, 经常碰到类似的场景, 比如使用 ArcGIS Server 提供的 Rest 服务进行地理查询, 如果用 Jscex 的话, 确实会方便不少。

  10. 老赵
    admin
    链接

    老赵 2011-12-20 09:06:08

    @张志敏

    有没有详细的说明或文档呢?我一直在收集各种示例。

  11. 银光小子
    210.75.15.*
    链接

    银光小子 2011-12-20 09:34:17

    不咋懂....

    嘻嘻 大概还过一个月的样子吧 我会开源一个Silverlight的小框架 到时候 请你看看哈 嘻嘻。

    我之所以做那个是因为我也觉得现在搞SIlverlight企业开发的兄弟们被禁锢 得不轻 呵呵。

  12. 银光小子
    210.75.15.*
    链接

    银光小子 2011-12-20 09:36:05

    @张志敏

    用Arcgis server,是开发WEBIGS对吧。为啥不用Silverlight开发呢??非要折腾自己用JavaScript开发!

  13. Sparkle
    61.144.146.*
    链接

    Sparkle 2011-12-20 09:39:37

    用回调实现的异步本来就很恶心,不知道为什么nodejs会热起来

  14. Wuvist
    27.41.135.*
    链接

    Wuvist 2011-12-20 10:39:32

    “Jscex这种方式对生产力是否有提高,我觉得就没必要纠结这点了,这些已经被论证过很多次了──不如想如何可以多写几个示例。” 我说的就是现在jscex提供的“模态对话框”示例不好,期待更多示例。

    “复制完整文件夹”,看上去也是纯后端node的例子,前端程序员木有此需求。

    如果说回调“困扰一代代程序员”,也包括前端程序员的话,jscex应该能够很容易找到很多有说服力的例子。但似乎老赵还是在四处在寻找;这是为什么?

  15. 老赵
    admin
    链接

    老赵 2011-12-20 10:47:54

    @Wuvist

    在我看来“模态对话框”也已经比较能说明问题了,可能你要求比较高(或者独特,例如十分看重代码行数),我问过一些前端同学(4个还是5个),他们都对Jscex的写法比较满意。当然例子还是会继续找的。

    当然,如果说我在寻找例子,不如说我在收集各种例子。例子什么的Jscex一直是有的,要慢慢文档化而已。我是在积极从社区收集信息,你却直接理解为我Jscex缺少使用价值。按照你的意思,似乎只要足够nb或是自信就能不需要社区力量了?我只能说这种过度解读实在挺没意思的。

  16. waynebaby
    210.22.108.*
    链接

    waynebaby 2011-12-20 10:54:39

    说起动画 要不要搞一个 WPF/SL 的 storyboard await 封装玩玩呢

  17. 老赵
    admin
    链接

    老赵 2011-12-20 10:55:35

    @waynebaby

    具体怎么说?

  18. Yunjing
    141.212.110.*
    链接

    Yunjing 2011-12-20 10:58:40

    Jscex想解决的问题很有意思。但这篇文章的推广思路有问题,因为它试图同时推广两个概念: 异步编程本身和Jscex对一般异步编程的改进。很多人理解第一个概念能带来的好处可能就有问题了,更何况是同时理解jscex带来的好处。是不是可以把两件事情分来来写,或者至少分成两个部分,分层次来讲解。

  19. 老赵
    admin
    链接

    老赵 2011-12-20 11:02:30

    @Yunjing

    我想想啊,但我其实没有想提异步编程的好处,我是假设大家已经有异步编程需求了,于是Jscex来帮助人们编程。

  20. 张志敏
    58.248.45.*
    链接

    张志敏 2011-12-20 11:29:02

    @银光小子

    确实是在用Silverlight, 我说的是如果用Jscex的话,确实可以方便很多。

    为了解决这类问题, 我曾经自己实现了一个 , async task emulator, 随着 Silverlight 5 的发布, 带来了 System.Threading.Tasks ,这类问题迎刃而解。

  21. 链接

    张志敏 2011-12-20 11:34:33

    楼主的博客用 Opera 无法发表回复, 提示说有奇怪的事情发生了, 请问是什么奇怪的事情呢?

    应该是bug吧, 希望抽时间调整一下。

  22. 老赵
    admin
    链接

    老赵 2011-12-20 11:38:14

    @张志敏

    接下来C#有await了算是内置Jscex了……

    没有Opera唉……有条件要测试一下……

  23. Wuvist
    27.41.135.*
    链接

    Wuvist 2011-12-20 12:47:39

    @老赵

    无论别人肿么解读,jscex能够拿更好例子秀出来就KO了嘛~

    现在文章里只能演示一些概念,只能以片段进行,所以看上去差距不是很明显。

    这可是你原话。

    如果我没有看错,玉伯也是说jscex很好,但就是在前端开发中找不到需求。

  24. waynebaby
    210.22.108.*
    链接

    waynebaby 2011-12-20 12:50:46

    现在 WPF/SL的动画都是回调型的嘛 封装成Task怎么样呢

    以前我们要做一个非常简单的弹珠台消砖块 或者ping 原本需要每一贞计算一次位置 这个不爽, 因为只有一个球作为碰撞体 在一段动画播放完 不会有预料之外的碰撞 完全可以做成一串storboard串联脚本 来保证动画连贯对吧,

    写成顺序的逻辑更好不是吗?

    While
    {
        while (不是撞向下方)
        {
            随便撞
            计算碰撞结果
        }
    
        判断是否撞板 
        撞了改方向
        没撞退出循环
    }
    
    播放飞出
    播放gameover
    
  25. 老赵
    admin
    链接

    老赵 2011-12-20 13:14:39

    @Wuvist

    唉,沟通好困难啊。我不是说了么,模态对话框我觉得挺好的,只是以你的“代码数量”作为标准似乎的确差距不大。不过例子大了又要说太麻烦了,所以你要从简单例子里看出问题来啊。玉伯是不知道Jscex可以怎么用,以为只有Node.js和AJAX才算异步了。如果我没记错,玉伯好像觉得模态对话框挺好的。

    算了我不陪你解读了,还是继续收集示例吧。

  26. 老赵
    admin
    链接

    老赵 2011-12-20 13:17:48

    @waynebaby

    是啊,我的那个bullet就是类似的写法的。你有那个消弹珠的示例吗?

  27. Wuvist
    27.41.135.*
    链接

    Wuvist 2011-12-20 13:35:25

    @老赵

    “代码数量”也不是我的标准,至少,微软在宣传新技术的时候也总喜欢说新技术比旧的可以少写很多代码,甚至完全不写代码不是?

    如果jscex可以秀出例子来说用了之后代码量更少,更简洁,相信可以更吸引眼球。

  28. 链接

    张志敏 2011-12-20 13:43:14

    @waynebaby

    这个想法好, 如果是 C# 的话, 我想用 yield 实现应该不难的

  29. 老赵
    admin
    链接

    老赵 2011-12-20 13:46:20

    @Wuvist

    我当然知道节省代码可以吸引眼球,但它不是Jscex的卖点,虽然在很多场景里它的确能节省代码,但我一直说Jscex的优势是更方便写代码,就算某个示例好像没有省多少代码,但还是体现出Jscex的价值。能拿给别人看的示例基本只能是片段代码,就算全省下了又能省多少?我不如多强调这里面隐含的错误处理的机制。

    就像我说C#比Java好,一直也说的是C#表达能力强,写代码顺畅,而不是代码行数少,虽然很多情况下它的确少。反倒一直是评论里的某个同学说“Java代码也没多几行”,比如上次说委托的问题,他就举出Java的事件代码这样说。但这都不是我关注的东西,把代码数量作为最重要的甚至是唯一标准,这就不是正确的方向了。

  30. waynebaby
    210.22.108.*
    链接

    waynebaby 2011-12-20 13:55:49

    我手里没有正好完整的实例 expression blend 里面有个消砖块的例子工程 但是不知道是不是基于动画的

  31. waynebaby
    210.22.108.*
    链接

    waynebaby 2011-12-20 13:59:04

    @张志敏

    是不难

    2.0时代最头疼的是异步方法方式太多 AE写出来很难看懂。4.0时代有Task了 直接Yield Task应该就没啥问题了。但是现在有更方便的方法async await了 不是嘛

  32. Wuvist
    27.41.135.*
    链接

    Wuvist 2011-12-20 14:23:46

    “代码顺畅” 这是很主观的,你提到的说“Java代码也没多几行”的那位同学是在说java更加畅顺对吧?

    $.ajax({
        url: "./getSomething",
        dataType: "text",
        success: function (data, textStatus, jqXHR) {
            ...
        },
        error: function (jqXHR, textStatus, errorThrow) {
            ...
        }
    });
    

    这对于很多人来讲,也是很畅顺。他们习惯了向左走。

    你站出来说 他们向左走的思维是受禁锢的,跟他们说还可以向右走,代码还可以写成:

    $.ajaxAsync = function (options) {
        return Task.create(function (t) {
    
            options.success = function (data, textStatus, jqXHR) {
                t.complete("success", {
                    data: data,
                    textStatus: textStatus,
                    jqXHR: jqXHR
                });
            }
    
            options.error = function (jqXHR, textStatus, errorThrow) {
                t.complete("failure", {
                    jqXHR: jqXHR,
                    textStatus: textStatus,
                    errorThrow: errorThrow
                });
            };
    
            $.ajax(options);
        });
    };
    

    我相信,大多数人看了之后第一反应都是懵了,肿么就代码变复杂了?错误处理?经典方法也有呀~

    只有在需要做大量连续、前后依赖的调用时,“经典方法”才会显得复杂,不可维护。我猜想上面的张志敏同学说的就是这样的情况。

    而这样的情况,我猜想在前端中并不普遍;仅有一两次ajax调用的情况,我觉得“经典方法”明显更加合适。

  33. pokka
    61.140.232.*
    链接

    pokka 2011-12-20 14:30:00

    大开眼界。

    邮寄会来吐槽,哥们多写点吧。

  34. 老赵
    admin
    链接

    老赵 2011-12-20 14:44:11

    @Wuvist

    你怎么把封装的代码拿来比较的?你为什么不把jQuery源代码或是Jscex源代码贴出来?关注的是怎么用,而不是jQuery,Jscex或绑定代码怎么实现。你要比较的应该是这段代码:

    var response = $await($.ajaxAsync({ url: "...", dataType: "text" }));
    // ...
    

    当然,Jscex不是完全不需要学习,一眼看上去不懂很正常,毕竟Jscex是创新产品。但用“需要学习”这种理由拒绝Jscex的人,我也没希望他们去使用Jscex。

    我之前说了,代码顺畅许多时候不是主观的,对于个人才有“习惯”一说,但我就是想要改变很多人习惯。觉得经典用法不错,错误处理也不错的人,那是不知道Jscex的用法,没有对比而已,顺畅与否是可以比较出来的。你可以说这些思维习惯是Jscex推广路上的障碍,讨论如何克服这些障碍有价值,但讨论Jscex本身又没有价值就没什么意义了。对了,那位同学才不关心Java代码顺不顺畅,只是个技术流氓在想尽各种办法扯蛋而已,在我这里很出名的。

    有点回到我第一次回复的东西了,希望不要再绕回去反复谈了……

  35. Wuvist
    27.41.135.*
    链接

    Wuvist 2011-12-20 15:00:02

    不好意思,ajax这个是我看错了。

  36. waynebaby
    210.22.108.*
    链接

    waynebaby 2011-12-20 15:18:53

    话说我在.net用 AE / await 做文件拷贝的例子的时候 会有一点不舒服。

    虽然都是异步操作 都用完成端口 但是coroutine的方式 是要求Parts之间是首尾相接的 所以两个stream 一定是读完了再写 写完了再读 不会同时读写吧。可能是最省线程资源的方式 但是不一定在这个级别上最快的方式吧。

    结果还是RX写这种最快不是吗

  37. waynebaby
    210.22.108.*
    链接

    waynebaby 2011-12-20 15:26:46

    不知道 Jscex 在浏览器中 或者是Node 上 会不会有类似的情况?

  38. 老赵
    admin
    链接

    老赵 2011-12-20 18:32:19

    @waynebaby

    如果写法一样自然效果也一样。你要边读边写就要借助个缓冲区,例如Agent(就是Erlang的那个Agent)实现:

    var keepReadingAsync = eval(Jscex.compile("async", function (buffer) {
        while (true) {
            var buffer = $await(readAsync());
            buffer.post(buffer);
        }
    }));
    
    var keepWritingAsync = eval(Jscex.compile("async", function (buffer) {
        while (true) {
            var buffer = $await(buffer.receive());
            $await(writeAsync(buffer));
        }
    }))
    
    var agent = new Jscex.Async.Agent();
    keepReadingAsync(agent).start();
    keepWritingAsync(agent).start();
    

    在Node.js,如果用Stream对象的话,它是内置缓冲区的,你要做的只是不断write。write是个同步方法,来不及写自己会暂时保存起来。

  39. @青山老妖_黄冠
    116.230.248.*
    链接

    @青山老妖_黄冠 2011-12-21 00:27:00

    贴这段comet模式的聊天室代码,

    var responseSet = [];
    
    app.use('/message/get',function(req, res){
        responseSet.push(res);
    
    }).use('/message/send',function(req, res){
        var message = {};
        message.username = req.cookies.username;
        message.content  = req.body.content;
        message.date = new Date();
    
        var messageStr = JSON.stringify(message);
        var responses = responseSet;
        responseSet = [];
    
        while(responses.length > 0)
            responses.shift().end(messageStr);
    
        res.end();
    });
    

    老赵觉得这个简单业务逻辑如何变成一个coroutine的模式。我的想法是在('/message/get')当中来一个eventbus,添加一个callback方法,

    comet

    这样的话就可以用jscex,不过我想来想去总觉得不太好。但有没有想比较有说服力的点,请老赵指点。

  40. 老赵
    admin
    链接

    老赵 2011-12-21 09:44:31

    @青山老妖_黄冠

    其实就是借助个Agent,Erlang的那种。

    var userAgents = { };
    var getAgent = function (username) {
        var agent = userAgents[username];
        if (agent) return;
    
        agent = new Jscex.Async.Agent();
        userAgents[username] = agent;
        return agent;
    }
    
    app.useAsync("/message/get", eval(Jscex.compile("async", function (req, res) {
        var username = req.cookies.username;
        // 直到收到消息才返回。
        var message = $await(getAgent(username).receive());
        res.write(message);
        res.end();
    })));
    
    app.use("/message/send", function (req, res) {
        var username = req.cookies.username;
        var message = ...;
        getAgent(username).post(message);
        res.end();
    });
    

    Jscex.Async.Agent其实是在jscex-async-agent.js里,不过估计已经不能跑了,因为前段时间修改了不少基础代码。你先看看代码意思,我有时间把Agent修复一下。

  41. @青山老妖_黄冠
    180.171.48.*
    链接

    @青山老妖_黄冠 2011-12-21 10:14:25

    好的,谢谢.

  42. 老赵
    admin
    链接

    老赵 2011-12-21 15:39:38

    @青山老妖_黄冠

    改好了,你可以去看看。

  43. kk
    116.231.9.*
    链接

    kk 2011-12-21 17:44:06

    惭愧啊 写了半个晚上的 汉诺塔 按你这个只要 几行代码

  44. halida
    96.44.160.*
    链接

    halida 2011-12-21 17:59:07

    这样的编程范式的确是应当的. 我有一个问题: Jscex是如何实现的? 是否修改了javascript的编译执行方式? 看来我要看看源码...

  45. 老赵
    admin
    链接

    老赵 2011-12-21 18:09:26

    @halida

    Jscex是以类库的方式使用的,只要标准的JavaScript执行引擎就行了。当然,它会把用户输入的代码转化成带回调函数的形式来执行。

  46. 老赵
    admin
    链接

    老赵 2011-12-21 18:11:00

    @kk

    是啊,用Jscex来把算法做成动画是十分顺手的,我知道的案例已经很多了,这种东西没有Jscex的确十分难写,更难读了。

  47. @青山老妖_黄冠
    180.171.48.*
    链接

    @青山老妖_黄冠 2011-12-22 15:54:20

    ok....

  48. 四不象
    116.226.38.*
    链接

    四不象 2011-12-23 15:28:35

    如果javascript支持协程的话,要实现这些也很容易

  49. 老赵
    admin
    链接

    老赵 2011-12-23 16:05:20

    @四不象

    如果JavaScript支持协程,你以为我还会做这些么……

  50. boxsir
    122.96.158.*
    链接

    boxsir 2011-12-23 17:29:02

    绝大部分人都是蠢货,博主无需追求得到所有人的认可。

  51. 老赵
    admin
    链接

    老赵 2011-12-23 23:57:00

    @boxsir

    不追求所有人,但追求相当部分人,否则就推广不出去了,呵呵。

  52. LayersSss
    113.108.140.*
    链接

    LayersSss 2012-01-01 14:01:27

    请问老赵,可不可以理解Jscex和Concurrent.Thread在面向编程范式上达到了一样的效果,就是让程序员以伪线程方式编程?而Concurrent.Thread是真正创造了子线程(暂且这么叫)的控制器,分时共享JavaScript的唯一线程,而Jscex依然只是让子线程排队共享Javascript的唯一线程?

  53. 老赵
    admin
    链接

    老赵 2012-01-01 15:55:32

    @LayersSss

    我不是很了解Concurrent.Thread,随便说几句。

    Jscex的目标是在编写异步程序的时候使用同步的表达方式。JavaScript永远只有一个线程,Jscex也没有创造子线程之类的概念,它的执行行为和平时使用回调的编程方式其实是完全一样的,不存在什么多任务分时之类的,其实就是普通的:没有到异步操作就一路执行到底,有异步操作那就发起异步操作。

    Concurrent.Thread它是创建了多线程的编程方式,跟Jscex完全不是一种东西,Jscex你可以认为是coroutine,没有多线程的概念。

  54. Cat Chen
    106.187.35.*
    链接

    Cat Chen 2012-01-09 13:43:58

    NPM 是支持 V8 Extension 的,所以如果你写成 V8 Extension,良好支持 NPM 的云平台都能支持。其中一个例子是 node-proxy,它就是一个 V8 Extension,允许你拦截任何对 Object 实例上未定义属性的访问,使得你可以好像 Ruby 或 Objective-C 一样重新路由这些访问。

    P.S. 国内那些没有真正虚拟化技术的平台可以死一边去。

  55. 老赵
    admin
    链接

    老赵 2012-01-09 21:43:45

    @Cat Chen

    就要看那些算是独立虚拟主机(预安装Node)还是Node托管服务了,前者自然什么都行,甚至装Java或PHP都行(比如那个啥,我老是记不起一些云服务名字)。

  56. 链接

    jinsikui 2012-01-19 13:19:40

    绝对支持老赵,国内程序员能自己写出套框架寥寥无几,祝愿这个将来和Jquery一样流行!

  57. 老赵
    admin
    链接

    老赵 2012-01-21 16:48:11

    @jinsikui

    Jscex是类库,跟jQuery一样,多谢。

  58. Learno
    183.60.103.*
    链接

    Learno 2012-02-03 00:14:50

    Jscex有依赖js的某些特有语法吗?存在移植到ActionScript3的可能性吗?

  59. 老赵
    admin
    链接

    老赵 2012-02-03 18:42:35

    @Learno

    函数需要能返回自身代码,还要有eval,所以似乎不能移植到AS3上。

  60. 何麟
    125.122.85.*
    链接

    何麟 2012-04-16 10:13:58

    我能马上想到这个解决 nested callbacks 造成的 less-readable code. 就像 ruby 里面用eventmachine时写的代码会不自然:you would have callbacks within callbacks. 而Ruby 的 fiber 这个东西解决了这类问题,跟jscex类似。有兴趣看这个链接

    喜欢这个lib能对程序员带来的好处。

  61. 链接

    free2bird 2012-04-19 15:18:07

    感谢老赵的例子,对异步理解不那么狭隘了。JSCEX是个好东西。

  62. 1804
    183.60.177.*
    链接

    1804 2012-11-10 00:21:57

    刚开始学,所以问一下. 如果不对请多批评. 嘿嘿

    比如说, 要实现 a和b两个操作按顺序执行 .

    按一般所做的,就是 a执行完 发通知到 b ,b接收到调用执行 . 是一个连环索一样的 来实现按顺序执行.

    但这里wind 的方式,是 按顺序执行a和b . a完成了就完成了, 和b的存在毫无实际关联, 也就不存在连环索的影响. 执行方式就像 for 循环 数组 对象一样, 循环完i,接着循环下一个i++. 能保证a完成之后执行b, 是因为a和b有一个控制他们的"循环体"-宿主(这个可能不对,只是我初学wind大致这么认为). 由他来保证实现.

    如果按之前的方式,连环锁是很难添加移除或者换位置, 这是很折磨人的(怨念),也很容易出现问题. 但是按 wind的 理念, 前后的操作,并无绝对的(连锁)关系, 只是 存在相对的 (返回值)关系 . 也就可以很随意的添加修改处理流程.

    顺序, 我觉得这个就我所理解的 自然 -- 起床之后,可以去刷牙,当然也可以去蹲坑.

    可能是一直以来,对 一切都基于事件驱动的 思维模式 太...

  63. pink
    116.90.82.*
    链接

    pink 2013-04-02 11:27:11

    不错,开拓了思维,不过你是不是被同步禁锢住了,个人觉得异步回调正是javascript的魅力所在。 也许有些其他语言转过来的人不理解吧,非要把javascript搞成他们以前玩别的语言的习惯而已。

  64. pink
    116.90.82.*
    链接

    pink 2013-04-02 11:30:15

    在推广Jscex的过程中,我发现有个比较明显的问题是,许多使用JavaScript的程序员已经习惯旧有的编程方式,甚至推崇一些据他们说很“漂亮”的模式。但在我看来,这其实跟许多GoF模式是在修补OO语言的不足有些类似,很多异步模式都只是因为JavaScript语言特性不足而设计出来的“权宜之计”。我们在传统JavaScript编程环境下并没有其他选择,单纯地认为这是“美”,说实话只不过是一种安慰罢了。

    哎,你也是在推广你自认为“漂亮”的模式而已。为啥你文章里攻击性语言这么多。去除这些糟粕那就完美了,都是精华。“权宜之计”........你还真自信

  65. 老赵
    admin
    链接

    老赵 2013-04-02 17:40:21

    @pink

    你先把异步跟回调的关系理清再说吧。

    我说的是异步仅仅是回调,你说的是异步等于回调,我的观点比你外延大,显然不会是我禁锢。

    异步回调再各种语言里都行,怎么就变JavaScript的精华了?是你接触的语言太少,不是我“从别的语言转过来”的缘故。

    还有这也叫攻击性语言?你也太脆弱了。

  66. pink
    116.90.82.*
    链接

    pink 2013-04-07 13:26:54

    异步回调

    是说异步和回调,当然在这里只是说的异步,不是说等于的意思,平时说习惯了而已。

    首先,我不是抨击wind的喷子,我上面也说了文章很好,开拓了思维。wind是一个很有意思的也很有意义的东西,甚至我都在用,在一些特定的环境也推荐给了别人用。之所以说是精华,是因为我个人很喜欢js的这种模式,并没有说只有js才有,另外你也说了“Jscex只是将这些现成的内容,从其他模式带到JavaScript编程领域上而已”,结合这些才有了我那句精华的话。

    在我眼里所有的模式都是特定环境下或者个人习惯所用,我只是希望不要一竿子推翻以前的模式来推广自己的模式,毕竟类库也好思想也好是写给适用的环境或者适用的人的。如果博主不是这个意思,那是我理解错误。

  67. 老赵
    admin
    链接

    老赵 2013-04-07 15:29:07

    @pink

    我的意思就是,回调不是JavaScript的优势,而是限制,别的语言也有回调,但是他们不满足于这点,发明了更好的模式,我把它带到了JavaScript里面去。你说回调是精华,我说那只是斯德哥尔摩或者没见过市面。

    我可没一竿子推翻以前的模式,我一直表示合适的时候自然也可以用回调,但是我绝对不同意说回调是精华,应该多用的之类的意思。JS里有了N种选择,大家还专注于回调,那才能说明问题,现在是你没选择。

  68. pink
    116.90.82.*
    链接

    pink 2013-04-08 11:17:09

    斯德哥尔摩。。。。 好吧 你赢了

  69. wq
    127.0.0.*
    链接

    wq 2013-10-10 20:44:18

    今天又翻出来这文章看看,如果是同时写C#和JS的人,肯定会爱不释手啊。但是感觉很多不喜欢wind.js的人,反对的理由像是有家餐馆推出了酱油蛋炒饭,XXX跑进去说我吃了这么多年的蛋炒饭都不放酱油,你不要来影响我对蛋炒饭的概念。

已自动隐藏某些不合适的评论内容(主题无关,争吵谩骂,装疯卖傻等等),如需阅读,请准备好眼药水并点此登陆后查看(如登陆后仍无法浏览请留言告知)。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我