目录

part1 deferred延迟对象

part2  when辅助方法

网盘源代码 链接: https://pan.baidu.com/s/1skAj8Jj 密码: izta

part1 deferred延迟对象

1 . deferred基于callbacks开发

使用callbacks完成异步:

<script src="js/jquery-2.0.3.js"></script>
<script>
var cb=$.Callbacks();
setTimeout(function(){
alert(111);
cb.fire();
},1000);
cb.add(function(){
alert(222);
})
//通过回调,先弹出111,再弹出222.
</script>

使用deferred完成异步:

<script src="js/jquery-2.0.3.js"></script>
<script>
var dfd=$.Deferred();
setTimeout(function(){
alert(111);
dfd.resolve();
})
dfd.done(function(){
alert(222);
})
//通过延迟对象,先弹出111,再弹出222.
</script>

我们看到,两者代码非常相似,其实deferred延迟对象本就是基于callbacks来开发的。deferred的resolve对应callbacks的fire,deferred的done对应callbacks的add。

延迟对象除了resolve状态,还有很多其他一些状态。

//3046行,这里定义了3套状态
//resolve代表成功,reject代表失败,notify代表通知与过程
var tuples = [
// action, add listener, listener list, final state
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],

类似:

<script src="js/jquery-2.0.3.js"></script>
<script>
var dfd=$.Deferred();
setTimeout(function(){
alert(111);
dfd.reject();
})
dfd.fail(function(){
alert(222);
})
//延迟失败,调用fail方法,那么最终的结果是先弹出111,再弹出222.
</script>

ajax内置了deferred对象哦。

 <script src="js/jquery-2.0.3.js"></script>
<script>
$.ajax("xxx.php").done(function(){
alert("成功!")
}).fail(function(){
alert("失败");
})
</script>

2.延迟对象的状态映射

我们知道deferred的成功状态包含resolve和done方法,失败状态包含reject和fail方法,进度中包含notify和progress方法。那么在源码中是如何映射起来的呢。

1.状态映射

//把相同状态的方法名都放到同一个数组里
var tuples = [
// action, add listener, listener list, final state
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
], 
//遍历数组,得到的元素也是一个数组,赋值给list
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
//状态字符串,即resolved和rejected。 // promise[ done | fail | progress ] = list.add
promise[ tuple[1] ] = list.add;
//把done、fail添加到add方法里 // 处理状态字符串
if ( stateString ) {
//
list.add(function() {
// 状态字符串,即resolved和rejected
state = stateString; // 一旦发生某个状态,那么对应的fire就会被禁用,或者progress就会上锁。
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
} // deferred[ resolve | reject | notify ]使用了callbacks的fire触发
deferred[ tuple[0] ] = function() {
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
return this;
};
deferred[ tuple[0] + "With" ] = list.fireWith;
});

2.once和memory功能

先来看callbacks。

<body>
<button id="btn">按钮</button>
<script src="js/jquery-2.0.3.js"></script>
<script>
var cb=$.Callbacks("memory");
cb.add(function(){
alert("aaa");
});
cb.fire();
$("#btn").click(function(){
alert("bbb");
})
//当我们点击按钮时,因为memory的记忆功能,点击按钮依然能弹出bbb。
</script> </body>

那么类似的,deferred也有memory功能,参照3048行的代码, jQuery.Callbacks("once memory") 。我们使用progress/notify一组来测试。

<script src="js/jquery-2.0.3.js"></script>
<script>
var dfd=$.Deferred();
setInterval(function(){
alert(111);
dfd.notify();
},1000);
dfd.done(function(){
alert("成功");
}).fail(function(){
alert("失败")
}).progress(function(){
alert("进度中")
});
//每隔一秒就会执行alert 111的任务,同时由于deferred的记忆功能,所以也会反复弹出"进度中"。
</script>

除了memory,那么once的意思是只有一次。

<script src="js/jquery-2.0.3.js"></script>
<script>
var dfd=$.Deferred();
setInterval(function(){
alert(111);
dfd.resolve();
},1000);
dfd.done(function(){
alert("成功");
}).fail(function(){
alert("失败")
}).progress(function(){
alert("进度中")
});
//无论resolve每隔一秒执行多少次,最终的done方法也只执行一次。
</script>

3.promise对象

1.promise对象与deferred对象的关系

在3095行开始的 jQuery.each( tuples, function( i, tuple ) { each方法里,会把 promise[ done | fail | progress ] = list.add done、fail、progress方法添加到callbacks的add方法里,这里使用了promise对象。3113行的 deferred[ tuple[0] ] = function() { 则使用了deferred对象。那么两者之间的关系是什么呢?

promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
//...
promise.promise( deferred );
  

我们从这些源码里可以看到,promise会继承给deferred对象。

promise = {
state: function() {
return state;
},
//.......

promise定义的方法包括state、always、then、promise、pipe、done、fail、progress等等,deferred定义的包括resolve、reject、notify方法,那么通过继承,deferred也就可以拥有所有的方法了。那么区别就是多出来的resolve、reject、notify方法。

这里有一个应用:

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
var dfd=$.Deferred();
setTimeout(function(){
dfd.resolve();
},1000);
return dfd;
}
var dfd=aaa();
dfd.done(function(){
alert("成功");
}).fail(function(){
alert("失败");
});
dfd.reject();
//由于reject的修改,那么弹出失败
</script>

可是如果我们返回的是promise对象,那么是没有暴露resolve或者reject等方法的,所以也就不存在可以修改状态了。

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
var dfd=$.Deferred();
setTimeout(function(){
dfd.resolve();
},1000);
return dfd.promise();
}
var dfd=aaa();
dfd.done(function(){
alert("成功");
}).fail(function(){
alert("失败");
});
dfd.reject();//报错
//弹出成功字符串 </script>

4.state状态的控制

state = "pending",
//一开始state就是pending“进行中”
promise = {
state: function() {
return state;
},
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ ],
stateString = tuple[ ]; // promise[ done | fail | progress ] = list.add
promise[ tuple[] ] = list.add; // Handle state
if ( stateString ) {
list.add(function() {
// state = [ resolved | rejected ]
state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock
}, tuples[ i ^ ][ ].disable, tuples[ ][ ].lock );
}

1.首先,来演示state的各个状态。

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
var dfd=$.Deferred();
alert(dfd.state());
//首先弹出deferred对象的状态
setTimeout(function(){
dfd.resolve();
alert(dfd.state());
//隔一秒之后已经成功了,那么弹出状态resolved
},1000);
return dfd.promise();
}
var dfd=aaa();
dfd.done(function(){
alert("成功");
}).fail(function(){
alert("失败");
});
</script>

结合deferred对象后面的代码,整个执行结果是先弹出pending,然后弹出字符串“成功”,最后弹出resolved。为什么使用的是state方法,其实源代码里揭示的十分清楚,它是一个function。

state: function() {//3054行

2.接下来看源代码是如何控制状态的改变的。

var tuples = [

                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],

我们看到,只有resolve和reject才有更新的状态哦,所以对于notify状态来说是没有stateString的,所以也就不会走if语句。

jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
//第4个元素正好是状态字符串 promise[ tuple[1] ] = list.add; // 处理状态字符串
if ( stateString ) {
//...
}

3.最后看重要的if语句是如何控制的。

if ( stateString ) {
list.add(function() {
// 将[ resolved | rejected ]赋值给state
state = stateString;
}, tuples[ i ^ 1 ][ 2 ].disable,
//因为tuple第3个元素就是回调jQuery.callbacks,那么禁用掉
tuples[ 2 ][ 2 ].lock );
//第3个元素也就是关于notify的,
//那么再数第3个元素也是回调jQuery.callbacks,锁住,就不会触发了
}

可以看到通过disable来禁用resolve和reject的回调,以及锁住notify的状态,那么状态一旦发生就不会改变了。

4.补充知识:

位运算符 tuples[ i ^ 1 ][ 2 ] 。

jQuery.each( tuples, function( i, tuple ) {
//3095行,说明i取值为0和1
<script>
alert(1^1);//弹出0
alert(0^1);//弹出1
</script>

那么也就是说如果i为0,那么取值为1,如果i为1,那么取值为0。所以对于禁用的原则是遍历到谁,那么其他的禁用(有点取反的意思在哦)。

4.deferred对象的工具方法always()、then()

1.always方法

always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
//源码对deferred对象,done方法、fail方法都调用传入的回调

那么看一个例子,就非常清晰了。

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
var dfd=$.Deferred();
setTimeout(function(){
dfd.reject();
},1000);
return dfd.promise();
}
var dfd=aaa();
dfd.always(function(){
alert("无论状态如何,调用这个回调函数");
});
</script>

2.then方法

3.promise方法

part2  when方法

1.when的使用

when方法在源码里是一个辅助方法。它的使用和deferred延迟对象是有区别的,deferred只能针对一个延迟对象进行操作,when能针对多个延迟对象进行判断。

1.when方法的延迟对象同时成功时触发done方法。

DEMO演示:

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
var dfd=$.Deferred();
dfd.resolve();
return dfd;
}
function bbb(){
var dfd=$.Deferred();
dfd.resolve();
return dfd;
}
$.when(aaa(),bbb()).done(function(){
alert("成功!");
})
</script>

浏览器显示结果:

2.when方法只要有一个延迟对象的状态是失败,那么就触发fail函数。

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
var dfd=$.Deferred();
dfd.resolve();
return dfd;
}
function bbb(){
var dfd=$.Deferred();
dfd.reject();
return dfd;
}
$.when(aaa(),bbb()).done(function(){
alert("成功!");
}).fail(function(){
alert("失败");
})
</script>

浏览器显示结果:

3.参数处理

无参、基本类型(比如123)、普通的函数并没有返回promise,那么就直接跳过参数。

<script src="js/jquery-2.0.3.js"></script>
<script>
$.when(123).done(function(){
alert("成功!");
}).fail(function(){
alert("失败");
})
//基本数据类型123就直接跳过啦,那么弹出成功字符串
</script>

那么既然一定要一个满足返回promise的函数,为什么要这样做呢,好处是能够传参。

<script src="js/jquery-2.0.3.js"></script>
<script>
$.when(123).done(function(){
alert(arguments[0]);
alert("成功!");
}).fail(function(){
alert("失败");
});
//弹出123字符串和成功字符串。
</script>

2.when方法的源码设计思想

我们先来假设一个例子,它有4个延迟对象,那么返回的是 return deferred.promise();//3181行 ,所以promise可以使用done方法。那么具体怎么做的呢。

$.when(aaa(),bbb(),ccc(),ddd()).done(function(){
alert("成功!");
});

先看一个图示。

3.when()无参的情况

DEMO演示:

<script src="js/jquery-2.0.3.js"></script>
<script>
$.when().done(function(){
alert("成功!");
}).fail(function(){
alert("失败");
})
//因为无参,那么跳过,最终弹出成功字符串。
</script>

进入源码,3134行,

var i = 0,
resolveValues = core_slice.call( arguments ),
//这里处理的是,往when方法传基本数据的方式
length = resolveValues.length,
//length就是传入的参数个数,那么无参就是0咯 remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
//由于length取0,那么说参数没有那么就取length也就是0.所以remaining计数器为0

计数器remaining为0,length为0.

deferred = remaining === 1 ? subordinate : jQuery.Deferred(),es
//也就不用执行咯
if ( length > 1 ) {
}
//if语句进不去
        if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
//remaining为0,进入if语句,所以deferred就被resolve了
return deferred.promise();

既然deferred被resolve了,那么关于 $.when().done(function(){ 就可以被执行了。

总之,如果无参,按照resolve来考虑。

4.when()的参数是基本类型,比如123

DEMO演示:

<script src="js/jquery-2.0.3.js"></script>
<script>
$.when(123).done(function(){
alert("成功!");
}).fail(function(){
alert("失败");
})
//基本数据类型123就直接跳过啦,那么弹出成功字符串
</script>

进入源码,那么length为1,

remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
//有参数,而且判断非promise函数,所以remaining计数器取0

接下来的updateFunc、length>1的if语句都不会走。!remaining的if语句会进去resolve。

if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
} return deferred.promise();

5.when方法的普通函数的参数

DEMO展示:

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
var dfd=$.Deferred();
dfd.resolve();
//即使定义了延迟对象,可是并没有返回promise对象
}
$.when(aaa()).done(function(){
alert("成功!");
}).fail(function(){
alert("失败");
})
//普通函数就直接跳过
</script>

进入源码,那么length为1,

remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
//有参数,而且判断非promise函数,所以remaining计数器取0

接下来的updateFunc、length>1的if语句都不会走。!remaining的if语句会进去resolve。

if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
} return deferred.promise();

4.when方法只返回一个promise

DEMO展示:

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
var dfd=$.Deferred();
dfd.resolve();
return dfd;
}
$.when(aaa()).done(function(){
alert("成功!");
}).fail(function(){
alert("失败");
})
//弹出成功字符串!
</script>

进入源码,length为1,remaining计数器为1.

remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
//有参数,而且jQuery能够判断为返回promise的function,那么最终计数器为1
//这是主要的deferred
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
//计数器既然为1,那么deferred为参数本身咯。

那么接下来的length>1的if语句就不会进入了。!remaining的if语句也不会进入。这个时候返回

return deferred.promise();
//自然是参数所代表的promise

4.when方法的复杂参数的处理

DEMO展示:

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
var dfd=$.Deferred();
dfd.resolve();
return dfd;
}
function bbb(){
var dfd=$.Deferred();
dfd.resolve();
return dfd;
}
function ccc(){
var dfd=$.Deferred();
dfd.resolve();
return dfd;
}
$.when(aaa(),bbb(),ccc()).done(function(){
alert("成功!");
}).fail(function(){
alert("失败");
})
//弹出成功字符串!
</script>

针对Demo所示的参数,length为3,remaining算下来就是length长度3.

remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
//既然length不等于1,逻辑运算的结果为1,那么||运算符就不用看了
//所以结果取length
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
//结果是jQuery.Deferred

if语句就要进入了。

// add listeners to Deferred subordinates; treat others as resolved
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
resolveContexts = new Array( length );
//length为多少,创建的数组就为多少
//根据length遍历
for ( ; i < length; i++ ) {
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
//遍历如果正确的参数,那么进入if语句 //与此同时else是把既有正常的参数,也有基本类型。
//那么就把非promise的情况计数器-1
resolveValues[ i ].promise()
.done( updateFunc( i, resolveContexts, resolveValues ) )
.fail( deferred.reject )
.progress( updateFunc( i, progressContexts, progressValues ) );
//对每个参数进行处理,可以看到done方法是所有的func都要成立
//而一旦某个fail了,就会导致master主deferred失败
//progress是所有的func的状态变化
} else {
--remaining;
}
}
}

通过遍历,对每个延迟对象进行处理。

jquery的2.0.3版本源码系列(7):3043行-3183行,deferred延迟对象,对异步的统一管理的更多相关文章

  1. jquery的2.0.3版本源码系列(1)总体结构

    为什么选择2.X版本,而不是1.X版本,因为2.X不兼容IE6/7/8,所以少了兼容代码,让我们更专注于jquery原理的代码. 一共有8830行. 1.1 匿名函数自执行 首先,匿名函数的作用是,把 ...

  2. jquery的2.0.3版本源码系列(6):2880-3042行,回调对象,对函数的统一管理

    目录 1 . 回调对象callbacks的演示 回调的使用有一点像事件绑定,先绑定好,等到有点击事件或者其他时就触发. <script src="js/jquery-2.0.3.js& ...

  3. jquery的2.0.3版本源码系列(3):96行-283行,给JQ对象,添加一些方法和属性

    jquery是面向对象的程序,面向对象就离不开方法和属性. 方法的简化 jQuery.fn=jQuery.prototype={ jquery: 版本 constructor: 修正指向问题 init ...

  4. jquery的2.0.3版本源码系列(2):21行-94行定义了一些变量和函数 jQuery=function(){}

    2.1.bug通过索引查询 这里的#13335是bug的索引,如何查询呢? 第一步,浏览器地址栏输入"https://bugs.jquery.com/". 第二步,在网页的搜索框里 ...

  5. jQuery源码02--(3043 , 3183) Deferred : 延迟对象 : 对异步的统一管理

    //延迟对象 jQuery.extend({ Deferred: function( func ) { var tuples = [//resolve完成.reject未完成.notify进行中类似于 ...

  6. jquery的2.0.3版本源码系列(4):285-348行,extend方法详解

    目录 1 . jquery extend的基本使用 通过285行的源码 jQuery.extend = jQuery.fn.extend = function() { ,extend方法要么是直接挂在 ...

  7. jquery的2.0.3版本源码系列(5):349-817行,extend添加的工具方法

    expando 生成唯一JQ字符串(内部)noconflict避免冲突isReady DOM是否加载完成(DOMContentLoaded)readyReady

  8. Kafka 0.10.1版本源码 Idea编译

    Kafka 0.10.1版本源码 Idea编译 1.环境准备 Jdk 1.8 Scala 2.11.12:下载scala-2.11.12.msi并配置环境变量 Gradle 5.6.4: 下载Grad ...

  9. spring各个版本源码

    各版本源码下载地址 http://maven.springframework.org/release/org/springframework/spring/

随机推荐

  1. bookStore第二篇【图书模块、前台页面】

    图书模块 分析 在设计图书管理的时候,我们应该想到:图书和分类是有关系的.一个分类可以对应多本图书. 为什么要这样设计?这样更加人性化,用户在购买书籍的时候,用户能够查看相关分类后的图书,而不是全部图 ...

  2. 举例让抽象问题具体化:包含min函数的栈

    定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数.在该栈中,调用min.push及pop的时间复杂度都是O(1). import java.util.Stack; public c ...

  3. Windows下chm转换为html的超简单方法

    摘要:通过调用Windows命令,将chm 文件转换为html 文件 概述:很多程序员朋友都会遇到这样的问题,看一个离线版的帮助文档(chm文件),总会产生一个索引文件(该文件的chw文件), 而且有 ...

  4. tomcat The specified JRE installation does not exist

    window->perferences->server->installed runtimes 里tomcat删掉,重新建立服务,再运行

  5. 判断字符串中是否包含指定的内容&&字符串截取方法比较说明

    1.使用indexOf()方法 方法说明: 作用:indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置(从前向后查找). 语法:stringObject.indexOf(searc ...

  6. ES6 Promise 对象

    Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大.它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Pro ...

  7. 实例讲解js正则表达式的使用

    前言:正则表达式(regular expression)反反复复学了多次,学了又忘,忘了又学,这次打算把基本的东西都整理出来,加强记忆,也方便下次查询. 学习正则表达式之前首先需要掌握记忆这些基本概念 ...

  8. MySQL or MariaDB 错误解决方法之报错代码1045

    phpMyAdmin登录报错:mysqli_real_connect(): (28000/1045): Access denied for user 'root'@'localhost' (using ...

  9. C#仪器数据文件解析-RTF文件

    RTF格式文件大家并不陌生,但RTF文件的编码.解码却很难,因为RTF文件是富文本格式的,即文件中除了包含文本内容,还包含文本的格式信息,而这些信息并没有像后来的docx等采用XML来隔离格式和内容, ...

  10. javascript(js)创建对象的模式与继承的几种方式

    1.js创建对象的几种方式 工厂模式 为什么会产生工厂模式,原因是使用同一个接口创建很多对象,会产生大量的重复代码,为了解决这个问题,产生了工厂模式. function createPerson(na ...