论坛首页 Web前端技术论坛

JS 异步按需载入和异步编程总结

浏览 7387 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-03-05  
JS 异步按需载入和异步编程总结
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- 授权: 共同创作2.0
- 作者: trydofor

本文使用了[[a9text => http://a9text.sf.net]]格式.
目标读者为,有一定js基础,并对异步按需载入有需求的人群.

0. 目录
^^^^^^^
[[<=$INDEX]]

1. 为什么异步按需载入
^^^^^^^^^^^^^^^^^^^^^
在web应用上,经常会根据需要来载入不同的JS/CSS/TEXT/HTML等.
在粗放型web开发,被用户体验取代的今天,按需载入也被用来榨干代码中的油水.
之所以我过多的关注按需载入,是因为sourceforge.net的访问速度有点可怜.
而且我一直努力的a9text有太多的area需要实时载入parser和render.

开发和本地使用,使用了XMLHttpRequest同步,但web应用中同步就等于死机
曾经和JSI的金大卫调侃说:知道浏览器厂商,啥时候能改善同步的体验不?

异步按需载入迫在眉睫,但JSI又不适合a9text.
JSI的着眼点在于已知,而a9text在于运行时和线程功能.
尽管老金告诉我JSI2.0瘦身版才5k,代码才500行.
无奈,只能自力更生 ...

2. 如何异步按需载入和异步编程
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
js不支持线程功能,更不用指望sleep和wait等功能.异步编程的不可预测性在js上徒增.
(http://www.neilmix.com/narrativejs/doc/设想不错,大家可以学习下)
挣扎过程中,考虑过timeout和interval,但不是最佳,也不是有效的方法.
解铃还得系令人,着眼点还得回到XMLHttpRequest的使用上来.

为此,a9text废除了a9engine,写了个专一的a9loader,用来提供异步开发的模式.
首先亮相的是a9loader提供了以下2个异步载入和编程的方法.
...........................................................
void A9Loader.asyncImportClass(String clzz)
    async import an js and export like java
    @param clzz (String) eg:'com.trydofor.a9text.parser.a9text'

void A9Loader.runAfterImport(Function func)
    run the func when all class imported
    @param func (Function)
...........................................................
asyncImportClass 用来 异步载入/初始化/包隔离/公共化 一个js类
runAfterImport 用来执行所有等待import完毕后的方法.

================== js : example ========================
A9Loader.asyncImportClass('com.trydofor.a9text.parser.a9text');
A9Loader.asyncImportClass('com.trydofor.a9text.render.html.a9text');

A9Loader.runAfterImport(function(){
    for(var i=0; i<a9PreText.length;i++){
        var a9dom = new A9Dom(null,A9Dom.type.root); //com.trydofor.a9text.A9Dom
        a9dom.setText(a9PreText[i]);
        a9dom.putInfo(A9Dom.type.root$path,A9Loader.getPagePath());
        a9DomHtml[a9dom.getId()] = a9PreHtml[i];
       
        new A9TextParser().parse(a9dom,function(){
            new A9TextRender().render(a9dom,function(){
                var a9htm = a9dom.getData();
                var preHtml = a9DomHtml[a9dom.getId()];
                var prePos  = bodyHtml.indexOf(preHtml);
                var headPos = bodyHtml.lastIndexOf('<',prePos);
                var footPos = bodyHtml.indexOf('>',prePos+preHtml.length)+1;
                bodyHtml = bodyHtml.substring(0,headPos) + a9htm + bodyHtml.substr(footPos);
                if(document.body) document.body.innerHTML="";
                document.write(bodyHtml);
                document.close();
            });
        });
    }
});
========================================================
上面的代码中,runAfterImport中还间接的调用了runAfterImport.
但结果都是缓存在一个队列中,等待import结束后顺序执行.

实现手法是这样的:
有一个task队列,里面有引用计数和几个队列.
发送一个async request的时候,计数加1,callback的时候,计数减1
同时呼叫一个检查计数的方法.
当最后一个callback执行时,计数等于0,于是开始处理各种队列.

3. 应用范围与不足
^^^^^^^^^^^^^^^^^
本应用属于另类,仅在特殊场合应用或学习使用,
一般的web开发,最好使用script tag或通常的ajax手法.

因为没有使用timeout和interval,框架完全基于一个基本假设,
就是所有async请求都能正常返回,如果其中有一个出现问题,
runAfterImport中排队的方法将永远不被执行.

其中的危险,主要来自浏览器的缓存,网络条件.

异步JS开发,请尽量回避.
希望a9loader和本文对需要的人有帮助.
   发表时间:2008-03-05  
没有明白为什么JSI不符合你的需求。。。
0 请登录后投票
   发表时间:2008-03-05  
首先,得声明,JSI是个不错的东西,生命力很强.
之所以说JSI不适合a9text,其根本原因有下:
1. 代码包管理,
我喜欢用java中package的概念来管理脚本.尽管有点多此一举,
但这样做是有点好处的,而这点与JSI中包的定义是不一样的.

2. 运行时异步载入.
JSI的$import的特点是,载入一个js就需要callback一次,
而a9text是运行时检测要载入的类,并全部载入后才可以执行.
如果使用JSI,基本上还需要写一个脚本管理器,并且编写代码
结构也变得复杂,难读,难维护,与其那样,不如定制一个'JSI'

3. 整体考虑.
JSI的最简版,也是要500行,a9loader算上注释也500行,而且都是自身需要的代码.
在JSI1.0的时代,a9text就有自己的loader(a9engine),处于整体的考虑,还是用
自己的最合适,不至于JSI定位扩大了,我也更正扩大

其实,不管是JSI还是a9loader,出发点和原理都是一样的,处于学习的目的,
我选择了自主,绝对没有说JSI不好的意思.

不知楼上认同否 ^_^
0 请登录后投票
   发表时间:2008-03-05  
1. jsi好像是可以做package的。只是也可以直接做脚本依赖。
2. 没有明白你的意思。你是指它的异步载入的api不符合你的需求么?
0 请登录后投票
   发表时间:2008-03-07  
再写下篇总结前,做个回复,我说的运行时异步载入是这样的:
1.载入/获得一段A9text文本.
|
V
2.异步载入a9textParser和xxxRender
|
V
3.parse过程中有xxxArea --->异步载入 xxxAreaParser --->(重复2.)
|
V
4.开始xxxRender
|
V
5.render过程中发现xxxArea --->异步载入 xxxAreaRender ---> (重复4.)
|
V
6.输出render结果

其中 3.和5.就是运行时异步载入的,
无法预先得知,所以不可以提前载入.
为了提高代码可读性,就出现了a9loader的框架来解决这样的问题.

知道论坛里高手比较多,这里献丑了,希望大家提提建议和改善意见.
a9loader本身比较清晰,希望读过代码(包括JSI)的在回贴.



0 请登录后投票
   发表时间:2008-03-08  
i c.

你需要的是模块的动态载入(按需载入),类似于classloader。
就你的需求来说,似乎不需要命名空间管理(也就是,似乎你的import是把一个包内的名字全部导入到global上)。这较为接近我已经停止开发的老PIES的功能(除了我只用同步不用异步之外)。

假如确实如此,则你用importXXX的命名稍有误导。import通常指静态的名称导入而和模块加载并无必然联系(虽然有些语言中不是如此,但是至少java是这样)。如果只是加载,建议你把api改为A9Loader.load('...')。然后你的A9Loader.runAfterImport(function(){...})可以改成A9Loader.onload = function () {...} 跟网页编程一样了,呵呵。

JSI(以及PIES)的主要目标都包括了命名空间和名称间的依赖管理,而不是一种纯粹的加载模块管理。比如JSI处理名称(对象间)依赖或者是script文件的依赖,而不直接处理“包”(据我看,A9Loader的包实际上是一个命名的加载模块,而不是java中作为namespace的package)的依赖。

不过事实上,它们是可以提供直接处理包依赖这样的功能的,PIES的草案设计就包含了这样的设计。但是怎样设计比较合理是个费心的事情。

还有,你说“JSI的$import的特点是,载入一个js就需要callback一次”,这大概就是一个API限制,但是没有理由在原理上有这种限制(我觉得JSI原来的addScriptDepedency或许接近你的需求,除了他的单位是script file而不是你的命名过的包【模块】)。你可问问jindw现在jsi是否能满足你的需求,呵呵。
0 请登录后投票
   发表时间:2008-03-08  
很感谢你的回复,今天是妇女节,差点累死我 ...

曾经和jindw简单的讨论过,但是JSI的定位和a9text不一样.
因此过多的让jindw关心a9text有点不合适,而且JSI2.0在耗很多心血.

代码是程序员最好的思想交流,我读了部分JSI代码,
借鉴一些合适的内容.

本文的目的主要是做个总结,让有特别需求的人少走弯路.
同时我也非常推荐使用JSI来解决JS开发带来的麻烦.

不过a9text的确比较特别(本位主义一下),算是另类应用.

关于方法命名,我暂时就不考虑修改啦,就张冠李戴凑合着用吧.

runAfterImport的执行时机,有点特别,不同于onload.
主要是可能别多次暂停和继续,即在一个runAfterImport执行方法中,又异步载入了一个类.
这时候,下一个runAfterImport,就需要等到这个类初始化完成才执行.
而a9text中就大量使用了这样的调用.

然后,补充一点,a9loader也是要解决脚本(包)依赖关系的,在__info__.js中描述
(a9text的命名是python和java的混合体,比较乱)
之所以使用包,而不是直接释放到全局,是因为a9text里面的impl都是相同的命名.
(算是自找麻烦)
比如.
com.trydofor.a9text.render.html.a9text.A9TextRender 和
com.trydofor.a9text.render.dom.a9text.A9TextRender 就是不同的impl
所以需要使用包来隔离,这样切换impl比较方便,多个共存也容易.
这是根本原因.
0 请登录后投票
   发表时间:2008-03-09  
思考了一下,咱不能做得过且过的营生,还是修改了import
改成:
void A9Loader.syncLoadClass(String clzz)
    sync import an js and export like java
    @param clzz (String) eg:'com.trydofor.a9text.parser.a9text'

void A9Loader.asyncLoadClass(String clzz)
    async import an js and export like java
    @param clzz (String) eg:'com.trydofor.a9text.parser.a9text'

void A9Loader.runAfterClassLoaded(Function func)
    run the func when all class imported
    @param func (Function)

我想这回能比较恰当些,看来论坛里讨论是很有积极意义的,
可惜,我发的帖子,只有福娃兄弟关注.

是我的帖子太水,还是没人关注...
学造自己的车,让别人去撞吧
0 请登录后投票
   发表时间:2008-03-30  
“runAfterImport的执行时机,有点特别,不同于onload. ”

我觉得正好合适啊。因为document onload是要等待所有资源(比如图片)的onload完成之后才触发的,呵呵呵。
0 请登录后投票
   发表时间:2008-04-01  
实际情况要复杂,不信,你可以访问下a9text.sf.net,看看源代码(右键即可以啦),基本上onload这个时间点,还没有载入大部分需要的js. onload不能解决的问题是,解析文档过程中,需要根据area名在配置文件中找到实现类并异步载入相关类,然后把结果合并到a9dom中,这个过程是比较难以把握的.

虽然异步载入,不是实际意义上的多线程,但是在这种竞争条件下,思考问题的方式和处理的方法,都会变得很诡异,要保证代码的可读性,是个比较棘手的问题,至少我在a9text的设计和编码过程中这样感想,js真是比较郁闷的事情
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics