Hello World
Spiga

浅谈Jscex的$await语义及异步任务模型

2011-05-10 22:29 by 老赵, 1945 visits

从某些程度上说,Jscex的是提供了“新语言”,只不过这种新语言和JavaScript长的一模一样,最多添加了一个$await操作这个语义而已。其他方面,JavaScript的各种语法都可以让Jscex编译,所以它基本可以说是个完备的方案。之前有朋友提出疑问,说$await只能执行单个任务,那么岂不是多个任务之间就出现了先后依赖关系?假如有三个任务:A和B可以并行,但C依赖前两者,ABC如果串行的话,系统的总耗时便不够理想了。其实Jscex并没有这种限制,因为它的任务模型和$await语义简单且具有深厚的理论基础,灵活、丰富而统一。

首先谈下$await的语义,有些朋友阅读示例代码,可能会觉得它表示“执行一个异步任务”。其实不然。$await的语义实际上只是“等待该任务结束”,同时:

  • 如果该任务没有运行,则启动该任务。
  • 如果该任务已经完成,则立即返回结果(或抛出异常)。

在Jscex的异步类库中,“异步任务”是独立的模型,它有自己的start或addListener等成员。一个Jscex异步函数的执行结果也是个异步任务对象,我们最终也是调用其start方法来启动这个任务。在一个Jscex函数内部,我们也可以手动地启动任务。例如:

function (taskA, taskB, taskC) {
    taskA.start();
    taskB.start();

    $await(taskA);
    $await(taskB);

    $await(taskC);
}

任务A和B的start方法会在调用后立即返回,并在两者都完成后,才会启动并等待C任务。以上便回答了之前那个朋友提出的问题。由于A和B没有依赖,我们便让其并行执行;C依赖前两者,于是便等A和B结束再启动,仅此而已。事实上,我们也可以这么做:

function (taskA, taskB, taskC) {
    $await(Jscex.Async.parallel(taskA, taskB));
    $await(taskC);
}

Jscex.Async.parallel是一个辅助函数,接受一堆异步任务,并返回一个新的异步任务,执行它表示并行地执行这些子任务,它也会在子任务都完成后才结束。这个异步任务模型十分简单,人人都能轻松地使用及扩展,也十分灵活。假如我们将刚才的需求换一下:C依赖于B,但A与前两者都独立,则可以编写这样的代码:

function (taskA, taskB, taskC) {
    taskA.start();

    $await(taskB.continueWith(taskC));
    $await(taskA);
}

在JavaScript中,我们只要为Jscex.Async.Task对象扩展一个continueWith方法,表示taskC将于taskB之后执行,并作为一个新任务返回即可。此类Future/Promise模型,以及Jscex的Monadic编译形式在异步编程领域中都有着经典的理论基础,因此Jscex在异步编程方面的支持可谓简单而统一。

Jscex一直在不断前进,如今Jscex又支持了一种$await语句形式,“赋值”:

hello.world = $await(...);

其实按理说,Jscex早该支持这种形式了,但可能是由于最早的思路有些过于借鉴F#的模式(无副作用,因此没有此类赋值),一不留意便疏忽至今了。JavaScript其实终究是一门更为“命令式”的语言,虽然Jscex由F#那里得到启发,但如今其具体实现和最终形式,却也是专为JavaScript而设计的,以确保其必要的功能(例如break,continue等中断逻辑流的语言特性)与性能。

Creative Commons License

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

Add your comment

6 条回复

  1. jeffwong
    117.79.73.*
    链接

    jeffwong 2011-05-10 23:01:49

    学习并支持一下。语法和调用方式都简洁易懂,但是...现在开发中都还用不上啊。

  2. 老赵
    admin
    链接

    老赵 2011-05-11 10:19:06

    @jeffwong

    写JavaScript哪有用不到异步的地方啊,呵呵。

  3. libinqq
    125.95.75.*
    链接

    libinqq 2011-05-11 11:29:51

    老赵如果能把.net 一些方便的表达式引用到 jscex就好了,比如linq, 让我们在一个集合里用表达式检索出自己想要的东东。

  4. 老赵
    admin
    链接

    老赵 2011-05-11 20:28:35

    @libinqq

    就用这种吧,感觉……虽然麻烦但多少还稍微能接受一点……

    coll.filter(function (t) { return t > 0; });
    
  5. 链接

    kerier2004 2011-05-11 22:47:30

    不知道在哪里提问题比较合适,冒昧在这里提了,我想做一个LIST实体类动态反射成XML的功能,功能已基本实现,但有一个小问题,当实体里面又有List实体类型的时候,不晓得怎么弄,不知道描述得够清晰不,不过代码应该能很明白说清,以下为代码片段:

    /// <summary>
    /// 把List转为XDocument
    /// </summary>
    /// <typeparam name="T">要转换的类型</typeparam>
    /// <param name="root">根目录</param>
    /// <param name="childNode">子节点</param>
    /// <param name="IL">数据列表</param>
    public static XDocument GenerateXml<T>(string root, string childNode, IList<T> IL)
    {
        XDocument doc = new XDocument(
            new XElement(root,
                new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
                new XAttribute(XNamespace.Xmlns + "xsd", "http://www.w3.org/2001/XMLSchema")
            )
        );
        doc.Element(root).Add(GetXmlList(IL, childNode));
        return doc;
    }
    /// <summary>
    /// 实体列表转为XElement列表
    /// </summary>
    /// <typeparam name="T">实体</typeparam>
    /// <param name="IL">列表</param>
    /// <param name="Node">节点</param>
    public static List<XElement> GetXmlList<T>(IList<T> IL, string Node)
    {
        T obj = Activator.CreateInstance<T>();
        Type type = obj.GetType();
        PropertyInfo[] pis = type.GetProperties();
        var listElement= (from list in IL
                          select new XElement(Node,
                          from p in pis
                          where p.PropertyType.Namespace == "System"
                          select new XElement(p.PropertyType.Name, p.GetValue(list, null))
                         ).ToList();
        return listElement;
    }
    
  6. bigbigsunrise
    111.140.83.*
    链接

    bigbigsunrise 2015-01-12 20:04:33

    博主你好,向您请教下: taskB依赖taskA,也就是您例子的简单版,我这样写:

    taskA().start(); $await(taskA); $await(taskB)

    可是不输出taskB的结果,给taskB加上(),似乎taskA和B是并发的,希望博主百忙之中能指导下,谢谢...

发表回复

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

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

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

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

使用Live Messenger联系我