博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一种模仿线程的Javascript异步模型设计&实现
阅读量:6991 次
发布时间:2019-06-27

本文共 4096 字,大约阅读时间需要 13 分钟。

jQuery中所支持的异步模型为:

  • Callbacks,回调函数列队。
  • Deferred,延迟执行对象。
  • Promise,是Deferred只暴露非状态改变方法的对象。

这些模型都很漂亮,但我想要一种更帅气的异步模型。

 

Thread?

我们知道链式操作是可以很好的表征运行顺序的(可以参考我的文章《》),然而通常基于回调函数或者基于事件监听的异步模型中,代码的执行顺序不清晰。

Callbacks模型实际上类似一个自定义事件的回调函数队列,当触发该事件(调用Callbacks.fire())时,则回调队列中的所有回调函数。

Deferred是个延迟执行对象,可以注册Deferred成功、失败或进行中状态的回调函数,然后通过触发相应的事件来回调函数。

这两种异步模型都类似于事件监听异步模型,实质上顺序依然是分离的。

当然Promise看似能提供我需要的东西,比如Promise.then().then().then()。但是,Promise虽然成功用链式操作明确了异步编程的顺序执行,但是没有循环,成功和失败分支是通过内部代码确定的。

个人认为,Promise是为了规范化后端nodejs中I/O操作异步模型的,因为I/O状态只有成功和失败两种状态,所以他是非常成功的。

但在前端,要么只有成功根本没有失败,要么不止只有两种状态,不应当固定只提供三种状态的方案,我觉得应该提供可表征多状态的异步方案。

这个大家可以在something more看到。

我想要一种类似于线程的模型,我们在这里称为Thread,也就是他能顺序执行、也能循环执行、当然还有分支执行。

 

顺序执行

线程的顺序执行流程,也就是类似于:

do1();do2();do3();

这样就是依次执行do1,do2,do3。因为这是异步模型,所以我们希望能添加wait方法,即类似于:

do1();wait(1000);    //等待1000msdo2();wait(1000);    //等待1000msdo3();wait(1000);    //等待1000ms

不使用编译方法的话,使用链式操作来表征顺序,则实现后的样子应当是这样的:

Thread().    //获取线程then(do1).    //然后执行do1wait(1000).    //等待1000msthen(do2).    //然后执行do2wait(1000).    //等待1000msthen(do3).    //然后执行do3wait(1000);    //等待1000ms

 

循环执行

循环这很好理解,比如for循环:

for(; true;){    dosomething();    wait(1000);}

进行无限次循环执行do,并且每次都延迟1000ms。则其链式表达应当是这样的:

Thread().    //获取线程loop(-1).    //循环开始,正数则表示循环正数次,负数则表示循环无限次    then(dosomething).    //然后执行do    wait(1000).    //等待1000msloopEnd();    //循环结束

这个可以参考后面的例子。 

 

分支执行

分支也就是if...else,比如:

if(true){    doSccess();}else{    doFail();}

那么其链式实现应当是:

Thread().    //获得线程right(true).    //如果表达式正确    then(doSccess).    //执行doSccessleft().    //否则    then(doFail).    //执行doFailleftEnd().    //left分支结束rightEnd();    //right分支结束

 

声明变量

声明变量也就是:

var a = "hello world!";

可被其它函数使用。那么我们的实现是:

Thread().    //得到线程define("hello world!").    //将回调函数第一个参数设为hello world!then(function(a){alert(a);});    //获取变量a,alert出来

 

顺序执行实现方案

Thread实际上是一个打包函数Fn队列。

所谓打包函数就是将回调函数打包后产生的新的函数,举个例子:

function package(callback){    return function(){        callback();        // 干其他事情    }}

这样我们就将callback函数打包起来了。

Thread提供一个fire方法来触发线程取出一个打包函数然后执行,打包函数执行以后回调Thread的fire方法。

那么我们就可以顺序执行函数了。

现在只要打包的时候设置setTimeout执行,则这个线程就能实现wait方法了。

 

循环执行实现方案

循环Loop是一个Thread的变形,只不过在执行里面的打包函数的时候使用另外一种方案,通过添加一个指针取出,执行完后触发Loop继续,移动指针取出下一个打包函数。

 

分支执行实现方案

分支Right和Left也是Thread的一种变形,开启分支的时候,主Thread会创建两个分支Right线程和Left线程,打包一个触发分支Thread的函数推入队列,然后当执行到该函数的时候判断触发哪个分支执行。

其中一个队列执行结束后回调主Thread,通知进行下一步。 

 

例子

由于该方案和wind-asycn非常相似,所以我们拿wind.js中的clock例子进行改造看看其中的差别吧。

wind.js中的例子:

    Clock - Wind.js Sample    

  

我的例子:

    Clock - asThread.js Sample    

  

Something more?

  • 将事件当成分支处理

我们提供了on方法将事件转成分支来执行。

举个例子页面有个按钮“点我”,但是我们希望打开页面5秒内单击没有效,5秒后显示“请点击按钮”后,单击才会出现“你成功点击了”。

使用on分支是这样的:

    on - asThread.js Sample    

自定义事件也可以哦,只要在.on时候传进去注册监听函数,和删除监听函数就行了。比如:

function addEvent(__elem, __type, __handler){    //添加监听}function removeEvent(__elem, __type, __handler){    //删除监听}Thread().on(ele, "success", addEvent, removeEvent).    then(function(){alert("成功!")}).onEnd().run();

当然实际上我们还可以注册多个事件分支。事件分支是并列的,也就是平级的事件分支没有现有顺序,所以我们能这样:

    on - asThread.js Sample    
  • 开辟多个线程

一个线程不够用?只要输入名字就能开辟或者得到线程了。

系统会自动初始化一个主线程,当不传参数时就直接返回主线程:

Thread() //得到主线程

但如果主线程正在用想开辟一个线程时,只要给个名字就行,比如:

Thread("hello")    //得到名字是hello的线程

那么下次再想用该线程时只要输入相同的名字就行了:

Thread("hello")    //得到hello线程

默认只最多只提供10个线程,所以用完记得删掉:

Thread("hello").del();
  • setImmediate

IE10已经提供了setImmediate方法,而其他现代浏览器也可以模拟该方法,其原理是推倒线程末端,使得浏览器画面能渲染,得到比setTimeout(0)更快的响应。

我们通过接口.imm来提供这一功能。比如:

Thread().imm(function(){alert("hello world")}).run();

这方法和.then(fn)不太一样,.then(fn)是可能阻塞当前浏览器线程的,但.imm(fn)是将处理推到浏览器引擎列队末端,排到队了在运行。

所以如果你使用多个Thread(伪多线程),而又希望确保线程是并行运行的,那么请使用.imm来替代.then。

当然对于老版IE,只能用setTimeout(0)替代了。

  • 分支参数可以是函数

分支Right传的参数如果只是布尔值肯定很不爽,因为这意味着分支是静态的,在初始化时候就决定了,但我们希望分支能在执行到的时候再判断是走Right还是Left,所以我们提供了传参可以是函数(但是函数返回值需要是布尔值,否则……╮(╯▽╰)╭也会转成布尔值的……哈哈)。比如:

fucntion foo(boolean){    return !boolean;}Thread().define(true).right(foo).    then(function(){
/*这里不会运行到*/}).rightEnd().run();

Enjoy yourself!!

 

项目地址

 

转载于:https://www.cnblogs.com/justany/archive/2013/01/25/2874602.html

你可能感兴趣的文章
[LeetCode] Permutation Sequence 序列排序
查看>>
MyBatis3: Could not find SQL statement to include with refid ‘
查看>>
scala spray 概念性内容的总结
查看>>
Spring中配置数据源的4种形式(转)
查看>>
分享9款极具创意的HTML5/CSS3进度条动画
查看>>
Windows 之 CMD命令
查看>>
SQL_Server2005自动备份与删除—维护计划
查看>>
第4周 页面限制8060 bytes
查看>>
设置myeclipse自动生成的author等注释
查看>>
【Cocos2d-Js基础教学 入门目录】
查看>>
【转】ActionBar的基本用法
查看>>
Linux 多线程通信
查看>>
PostgreSQL服务端监听设置及client连接方法
查看>>
Javascript事件总结
查看>>
(原创)speex与wav格式音频文件的互相转换(二)
查看>>
C#中模拟用户登陆SharePoint网站
查看>>
用css3实现各种图标效果(1)
查看>>
Bind("入库日期", "{0:yyyy-MM-dd}") 关于asp.net格式化数据库日期字符串
查看>>
TortoiseSvn客户端出现Http state 405 'Method Not Allowed' 的解决办法
查看>>
layoutSubviews总结
查看>>