javascript 异步编程2

javascript 异步编程2

好像有这么一句名言——”每一个优雅的接口,背后都有一个龌龊的实现”。最明显的例子,jQuery。之所以弄得这么复杂,因为它本来就是那复杂。虽然有些实现相对简明些,那是它们的兼容程度去不了那个地步。当然,世上总有例外,比如mootools,但暴露到我们眼前的接口,又不知到底是那个父类的东西,结构清晰但不明撩。我之所以说这样的话,因为异步列队真的很复杂,但我会尽可能让API简单易用。无new实例化,不区分实例与类方法,链式,等时髦的东西都用上。下面先奉上源码:

;(function(){
 var dom = this.dom = this.dom || {
 mix : function(target, source ,override) {
 var i, ride = (override === void 0) || override;
 for (i in source) {
 if (ride || !(i in target)) {
 target[i] = source[i];
 }
 }
 return target;
 }
 }
 //
 //=======================异步列队模块===================================
 var Deferred = dom.Deferred = function (fn) {
 return this instanceof Deferred ? this.init(fn) : new Deferred(fn)
 }
 var A_slice = Array.prototype.slice;
 dom.mix(Deferred, {
 get:function(obj){//确保this为Deferred实例
 return obj instanceof Deferred ? obj : new Deferred
 },
 ok : function (r) {//传递器
 return r
 },
 ng : function (e) {//传递器
 throw e
 }
 });
 Deferred.prototype = {
 init:function(fn){//初始化,建立两个列队
 this._firing = [];
 this._fired = [];
 if(typeof fn === "function")
 return this.then(fn)
 return this;
 },
 _add:function(okng,fn){
 var obj = {
 ok:Deferred.ok,
 ng:Deferred.ng,
 arr:[]
 }
 if(typeof fn === "function")
 obj[okng] = fn;
 this._firing.push(obj);
 return this;
 },
 then:function(fn){//_add的包装方法1,用于添加正向回调
 return Deferred.get(this)._add("ok",fn)
 },
 once:function(fn){//_add的包装方法2,用于添加负向回调
 return Deferred.get(this)._add("ng",fn)
 },
 wait:function(timeout){
 var self = Deferred.get(this);
 self._firing.push(~~timeout)
 return self
 },
 _fire:function(okng,args,result){
 var type = "ok",
 obj = this._firing.shift();
 if(obj){
 this._fired.push(obj);//把执行过的回调函数包,从一个列队倒入另一个列队
 var self = this;
 if(typeof obj === "number"){//如果是延时操作
 var timeoutID = setTimeout(function(){
 self._fire(okng,self.before(args,result))
 },obj)
 this.onabort = function(){
 clearTimeout(timeoutID );
 }
 }else if(obj.arr.length){//如果是并行操作
 var i = 0, d;
 while(d = obj.arr[i++]){
 d.fire(args)
 }
 }else{//如果是串行操作
 try{//
 result = obj[okng].apply(this,args);
 }catch(e){
 type = "ng";
 result = e;
 }
 this._fire(type,this.before(args,result))
 }
 }else{//队列执行完毕,还原
 (this.after || Deferred.ok)(result);
 this._firing = this._fired;
 this._fired = [];
 }
 return this;
 },
 fire:function(){//执行正向列队
 return this._fire("ok",this.before(arguments));
 },
 error:function(){//执行负向列队
 return this._fire("ng",this.before(arguments));
 },
 abort:function(){//中止列队
 (this.onabort || Deferred.ok)();
 return this;
 },
 //每次执行用户回调函数前都执行此函数,返回一个数组
 before:function(args,result){
 return result ? result instanceof Array ? result : [result] : A_slice.call(args)
 },
 //并行操作,并把所有的子线程的结果作为主线程的下一个操作的参数
 paiallel : function (fns) {
 var self = Deferred.get(this),
 obj = {
 ok:Deferred.ok,
 ng:Deferred.ng,
 arr:[]
 },
 count = 0,
 values = {}
 for(var key in fns){//参数可以是一个对象或数组
 if(fns.hasOwnProperty(key)){
 (function(key,fn){
 if (typeof fn == "function")
 fn = Deferred(fn);
 fn.then(function(value){
 values[key] = value;
 if(--count <= 0){
 if(fns instanceof Array){//如果是数组强制转换为数组
 values.length = fns.length;
 values = A_slice.call(values)
 }
 self._fire("ok",[values])
 }
 }).once(function(e){
 self._fire("ng",[e])
 });
 obj.arr.push(fn);
 count++
 })(key,fns[key])
 }
 }
 self.onabort = function(){
 var i = 0, d;
 while(d = obj.arr[i++]){
 d.abort();
 }
 }
 self._firing.push(obj);
 return self
 },
 //处理相近的迭代操作
 loop : function (obj, fn, result) {
 obj = {
 begin : obj.begin || 0,
 end : (typeof obj.end == "number") ? obj.end : obj - 1,
 step : obj.step || 1,
 last : false,
 prev : null
 }
 var step = obj.step,
 _loop = function(i,obj){
 if (i <= obj.end) {
 if ((i + step) > obj.end) {
 obj.last = true;
 obj.step = obj.end - i + 1;
 }
 obj.prev = result;
 result = fn.call(obj,i);
 Deferred.get(result).then(_loop).fire(i+step,obj);
 }else{
 return result;
 }
 }
 return (obj.begin <= obj.end) ? Deferred.get(this).then(_loop).fire(obj.begin,obj) : null;
 }
 }
 //将原型方法转换为类方法
 "loop wait then once paiallel".replace(/\w+/g, function(method){
 Deferred[method] = Deferred.prototype[method]
 });
})();

Deferred提供的接口其实不算多,then once loop wait paialle就这五个,我们可以new一个实例出来,用它的实例方法,可以直接用类名加方法名,其实里面还是new了一个实例。另外,还有两个专门用于复写的方法,before与after。before执行于每个回调函数之前,after执行于所有回调之后,相当于complete了。既然是列队,就有入队操作与出队操作,我不可能使用queue与dequeue这样土的命名。queue换成两个时间状语,then与once,相当于jQuery的done、fail,或dojo的addCallback、addErrback。dequeue则用fire与error取替,jQuery1.5的beta版也曾经用过fire,不知为何换成resolve这样难记的单词。好了,我们先不管其他API,现在就试试身手吧。

 var log = function (s) {
 window.console && window.console.log(s);
 }
 dom.Deferred(function () {
 log(1);//1
 })
 .then(function () {
 log(2);//2
 })
 .then(function () {
 log(3);//3
 })
 .fire();
//如果不使用异步列队,实现这种效果,就需要用套嵌函数
/*
 var fun = function(fn){
 fn()
 };
 fun(function(){
 log(1);
 fun(function(){
 log(2);
 fun(function(){
 log(3);
 })
 });
 });
*/

当然,现在是同步操作。注意,第一个回调函数是作为构造器的参数传入,可以节约了一个then^_^。

默认如果回调函数存在返回值,它会把它作为下一个回调函数的参数传入,如:

 dom.Deferred(function (a) {
 a += 10
 log(a);//11
 return a
 })
 .then(function (b) {
 b += 12
 log(b);//23
 return b
 })
 .then(function (c) {
 c += 130
 log(c);//153
 })
 .fire(1);

我们可以重载before函数,让它的结果不影响下一个回调函数。在多投事件中,我们也可以在before中定义,如果返回false,就中断队列了。

我们再来看它如何处理异常。dom.Deferred的负向列队与jQuery的是完全不同的,jQuery的只是正向列队的一个克隆,而在dom.Deferred中,负向列队只是用于突发情况,是配角。

 dom.Deferred(function () {
 log(1111111111)//11111111111
 }).
 then(function () {
 throw "error!";//发生错误
 }).
 then(function (e) {//这个回调函数不执行
 log(e+"222222")
 return 2222222
 }).
 once(function(e){//直到 遇上我们自定义的负向回调
 log(e+'333333')//error!333333
 return 333333333
 }).
 then(function (c) {//回到正向列队中
 log(c)//33333333
 }).
 fire()

<!doctype html> <html> <head> <title>异步列队 by 司徒正美 </title> <script src=”http://files.cnblogs.com/rubylouvre/dom.Deferred.js”> </script> <script> var log = function(a){ var div = document.createElement(“div”); div.innerHTML = a; document.body.appendChild(div); } window.onload = function(){ dom.Deferred(function () { log(1111111111)//11111111111 }). then(function () { throw “error!”;//发生错误 }). then(function (e) {//这个回调函数不执行 log(e+”222222″) return 2222222 }). once(function(e){//直到 遇上我们自定义的负向回调 log(e+’333333′)//error!333333 return 333333333 }). then(function (c) {//回到正向列队中 log(c)//33333333 }). fire() } </script> </head> <body> </body> </html>

运行代码

上面几个例子严格来说是同步执行,想实现异步就要用到setTimeout。当然除了setTimeout,我们还有许多方案,img.onerror script.onreadystatechange script.onload xhr.onreadystatechange self.postMessage……但它们 都有一个缺点,就是不能指定回调函数的执行时间。更何况setTimeout是没有什么兼容问题,如img.onerrot就不能用于IE6-8,postMessage虽然很快,但只支持非常新的浏览器版本。我说过,异步就是延时,延时就是等待,因此这方法叫做wait。

 dom.Deferred(function(){
 log(1)
 }).wait(1000).then(function(){
 log(2)
 }).wait(1000).then(function(){
 log(3)
 }).wait(1000).then(function(){
 log(4)
 }).fire()

<!doctype html> <html> <head> <title>异步列队 by 司徒正美 </title> <script src=”http://files.cnblogs.com/rubylouvre/dom.Deferred.js”> </script> <script> var log = function(a){ var div = document.createElement(“div”); div.innerHTML = a; document.body.appendChild(div); } window.onload = function(){ dom.Deferred(function(){ log(1) }).wait(1000).then(function(){ log(2) }).wait(1000).then(function(){ log(3) }).wait(1000).then(function(){ log(4) }).fire() } </script> </head> <body> </body> </html>

运行代码

好了,我们看异步列队中最难的部分,并行操作。这相当于模拟线程了,两个不相干的东西各自做自己的事,互不干扰。当然在时间我们还是能看出先后顺序来的。担当这职责的方法为paiallel。

 dom.Deferred.paiallel([function(){
 log("司徒正美")
 return 11
 },function(i){
 log("上官莉绮")
 return 12
 },function(i){
 log("朝沐金风")
 return 13
 }]).then(function(d){
 log(d)
 }).fire(10)

<!doctype html> <html> <head> <title>异步列队 by 司徒正美 </title> <script src=”http://files.cnblogs.com/rubylouvre/dom.Deferred.js”> </script> <script> var log = function(a){ var div = document.createElement(“div”); div.innerHTML = a; document.body.appendChild(div); } window.onload = function(){ dom.Deferred.paiallel([function(){ log(“司徒正美”) return 11 },function(i){ log(“上官莉绮”) return 12 },function(i){ log(“朝沐金风”) return 13 }]).then(function(d){ log(d) }).fire(10) } </script> </head> <body> </body> </html>

运行代码

不过,上面并没有用到异步,都是同步,这时,paiallel就相当于一个map操作。

 var d = dom.Deferred
 d.paiallel([
 d.wait(2000).then(function(a){
 log("第1个子列队");
 return 123
 }),
 d.wait(1500).then(function(a){
 log("第2个子列队");
 return 456
 }),
 d.then(function(a){
 log("第3个子列队")
 return 789
 })]).then(function(a){
 log(a)
 }).fire(3000);

<!doctype html> <html> <head> <title>异步列队 by 司徒正美 </title> <script src=”http://files.cnblogs.com/rubylouvre/dom.Deferred.js?1111111″> </script> <script> var log = function(a){ var div = document.createElement(“div”); div.innerHTML = a; document.body.appendChild(div); } window.onload = function(){ var d = dom.Deferred d.paiallel([ d.wait(2000).then(function(a){ log(“第1个子列队”); return 123 }), d.wait(1500).then(function(a){ log(“第2个子列队”); return 456 }), d.then(function(a){ log(“第3个子列队”) return 789 })]).then(function(a){ log(a) }).fire(3000); } </script> </head> <body> </body> </html>

运行代码

最后要介绍的是loop方法,它只要改变一下就能当作animate函数使用。

 d.loop(10, function(i){
 log(i);
 return d.wait(500)
 });

<!doctype html> <html> <head> <title>异步列队 by 司徒正美 </title> <script src=”http://files.cnblogs.com/rubylouvre/dom.Deferred.js?1111111″> </script> <script> var log = function(a){ var div = document.createElement(“div”); div.innerHTML = a; document.body.appendChild(div); } window.onload = function(){ var d = dom.Deferred d.loop(10, function(i){ log(i); return d.wait(500) }); } </script> </head> <body> </body> </html>

运行代码

添加多个列队,让它们交错进行,模拟“多线程”效果。

 d.loop(10, function(i){
 log("第一个列队的第"+i+"个操作");
 return d.wait(100)
 });
 d.loop(10, function(i){
 log("第二个列队的第"+i+"个操作");
 return d.wait(100)
 });
 d.loop(10, function(i){
 log("第三个列队的第"+i+"个操作");
 return d.wait(100)
 });

<!doctype html> <html> <head> <title>异步列队 by 司徒正美 </title> <script src=”http://files.cnblogs.com/rubylouvre/dom.Deferred.js?1111111″> </script> <script> var log = function(a){ var div = document.createElement(“div”); div.innerHTML = a; document.body.appendChild(div); } window.onload = function(){ var d = dom.Deferred; d.loop(10, function(i){ log(“第一个列队的第”+i+”个操作”); return d.wait(100) }); d.loop(10, function(i){ log(“第二个列队的第”+i+”个操作”); return d.wait(100) }); d.loop(10, function(i){ log(“第三个列队的第”+i+”个操作”); return d.wait(100) }); } </script> </head> <body> </body> </html>

运行代码

 

 

转载自:http://www.cnblogs.com/rubylouvre/archive/2011/03/18/1984336.html 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/110644.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 聊一聊Zabbix都监控哪些参数

    聊一聊Zabbix都监控哪些参数面试中的话,经常被问到技术方面的问题,也就是知识点的掌握程度,如果你准备充分的话,这个到不难,但一些开放性的东西,你可能答得就不是很好,便来到了我们的正题Zabbix监控哪些参数呢?这个范围是比较大、比较开放的,阔以以点带面的回答,也可概括性的回答,下面列举一些数据库:磁盘使用情况、内存使用情况、并发链接数量 数据库增删改查的频率、主从状态、缓冲池web:web服务是否正常、订单是否能正常下单注册是否正常、服务的响应时间、服务的并发量磁盘:使用率、block数,Inode数

  • Centos7中通过docker安装mysql

    Centos7中通过docker安装mysql

  • Vba菜鸟教程[通俗易懂]

    Vba菜鸟教程[通俗易懂]文章目录Vba菜鸟教程编辑器宏vba基本语法运算符变量语句简写语句sub语句调用语句退出语句分支语句循环语句判断语句公式与函数在单元格输入公式利用单元格公式返回值调用工作表函数利用vba函数自定义函数操作对象操作工作簿操作工作表操作单元格事件工作簿事件工作表事件控件按钮弹窗输入框附表对齐方式字体格式填充Vba菜鸟教程官方文档:https://docs.microsoft.com/zh-cn/o…

  • 如何从jdbc中获取数据库建表语句信息(表字段名称/表字段类型/表字段注释信息/表字段长度等等)

    如何从jdbc中获取数据库建表语句信息(表字段名称/表字段类型/表字段注释信息/表字段长度等等)*如何从jdbc中获取数据库建表语句信息(表字段名称/表字段类型/表字段注释信息/表字段长度等等)*1,表字段名称*2,表字段类型*3,表字段注释信息这里介绍3种方式,如下:第一种方式:执行sql语句获取select*fromuser_pop_infowhere1=2第二种方式:执行sql语句获取showcreatetableuser_pop_info第二种方式:直接从jdbc数据库连接Connection实例中获取三种方式获取的数据有一些区…

  • 计算机无法识别ANSI编码文件里的中文导致乱码「建议收藏」

    计算机无法识别ANSI编码文件里的中文导致乱码「建议收藏」问题现象最近远程协助一个用户的电脑(TeamView竟然连接不上,只好用QQ远程啦),原来ANSI编码的ini文件里的中文是正常的;用着用着,某一天就乱码了,无法识别ANSI编码文件里的中文。创建ANSI编码的ini配置文件,输入中文后保存会提示“…该文件有Unicode格式的字符,当文件保存为ANSI编码的文本时,该字符将丢失…”的问题。原因Unicode和ANSI是两种不同的字符编…

  • JavaScript语言知识收藏

    接触Web开发也已经有一段时间了,对javascript的认识也比以前有了更加深入的认识了,所以觉得应该整理一下。一、JavaScript不支持函数(方法)的重载,用一个例子证明如下:function

    2021年12月20日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号