源码注释

  

//     Zepto.js
// (c) 2010-2015 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.
//
// Some code (c) 2005, 2013 jQuery Foundation, Inc. and other contributors ;(function($){
var slice = Array.prototype.slice function Deferred(func) { //元组:描述状态、状态切换方法名、对应状态执行方法名、回调列表的关系
//tuple引自C++/python,和list的区别是,它不可改变 ,用来存储常量集
var tuples = [
// action, add listener, listener list, final state
[ "resolve", "done", $.Callbacks({once:, memory:}), "resolved" ],
[ "reject", "fail", $.Callbacks({once:, memory:}), "rejected" ],
[ "notify", "progress", $.Callbacks({memory:}) ]
],
state = "pending", //Promise初始状态 //promise对象,promise和deferred的区别是:
/*promise只包含执行阶段的方法always(),then(),done(),fail(),progress()及辅助方法state()、promise()等。
deferred则在继承promise的基础上,增加切换状态的方法,resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith()*/
//所以称promise是deferred的只读副本
promise = {
/**
* 返回状态
* @returns {string}
*/
state: function() {
return state
},
/**
* 成功/失败状态的 回调调用
* @returns {*}
*/
always: function() {
deferred.done(arguments).fail(arguments)
return this
},
/**
*
* @returns promise对象
*/
then: function(/* fnDone [, fnFailed [, fnProgress]] */) {
var fns = arguments //注意,这无论如何都会返回一个新的Deferred只读副本,
//所以正常为一个deferred添加成功,失败,千万不要用then,用done,fail
return Deferred(function(defer){
$.each(tuples, function(i, tuple){
//i==0: done i==1: fail i==2 progress
var fn = $.isFunction(fns[i]) && fns[i] //执行新deferred done/fail/progress
deferred[tuple[]](function(){
//直接执行新添加的回调 fnDone fnFailed fnProgress
var returned = fn && fn.apply(this, arguments) //返回结果是promise对象
if (returned && $.isFunction(returned.promise)) {
//转向fnDone fnFailed fnProgress返回的promise对象
//注意,这里是两个promise对象的数据交流
//新deferrred对象切换为对应的成功/失败/通知状态,传递的参数为 returned.promise() 给予的参数值
returned.promise()
.done(defer.resolve)
.fail(defer.reject)
.progress(defer.notify)
} else {
var context = this === promise ? defer.promise() : this,
values = fn ? [returned] : arguments
defer[tuple[] + "With"](context, values)//新deferrred对象切换为对应的成功/失败/通知状态
}
})
})
fns = null
}).promise()
}, /**
* 返回obj的promise对象
* @param obj
* @returns {*}
*/
promise: function(obj) {
return obj != null ? $.extend( obj, promise ) : promise
}
}, //内部封装deferred对象
deferred = {} //给deferred添加切换状态方法
$.each(tuples, function(i, tuple){
var list = tuple[],//$.Callback
stateString = tuple[]// 状态 如 resolved //扩展promise的done、fail、progress为Callback的add方法,使其成为回调列表
//简单写法: promise['done'] = jQuery.Callbacks( "once memory" ).add
// promise['fail'] = jQuery.Callbacks( "once memory" ).add promise['progress'] = jQuery.Callbacks( "memory" ).add
promise[tuple[]] = list.add //切换的状态是resolve成功/reject失败
//添加首组方法做预处理,修改state的值,使成功或失败互斥,锁定progress回调列表,
if (stateString) {
list.add(function(){
state = stateString //i^1 ^异或运算符 0^1=1 1^1=0,成功或失败回调互斥,调用一方,禁用另一方
}, tuples[i^][].disable, tuples[][].lock)
} //添加切换状态方法 resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith()
deferred[tuple[]] = function(){
deferred[tuple[] + "With"](this === deferred ? promise : this, arguments)
return this
}
deferred[tuple[] + "With"] = list.fireWith
}) //deferred继承promise的执行方法
promise.promise(deferred) //传递了参数func,执行
if (func) func.call(deferred, deferred) //返回deferred对象
return deferred
} /**
*
* 主要用于多异步队列处理。
多异步队列都成功,执行成功方法,一个失败,执行失败方法
也可以传非异步队列对象 * @param sub
* @returns {*}
*/
$.when = function(sub) {
var resolveValues = slice.call(arguments), //队列数组 ,未传参数是[]
len = resolveValues.length,//队列个数
i = ,
remain = len !== || (sub && $.isFunction(sub.promise)) ? len : , //子def计数
deferred = remain === ? sub : Deferred(),//主def,如果是1个fn,直接以它为主def,否则建立新的Def
progressValues, progressContexts, resolveContexts,
updateFn = function(i, ctx, val){
return function(value){
ctx[i] = this //this
val[i] = arguments.length > ? slice.call(arguments) : value // val 调用成功函数列表的参数
if (val === progressValues) {
deferred.notifyWith(ctx, val) // 如果是通知,调用主函数的通知,通知可以调用多次
} else if (!(--remain)) { //如果是成功,则需等成功计数为0,即所有子def都成功执行了,remain变为0,
deferred.resolveWith(ctx, val) //调用主函数的成功
}
}
} //长度大于1,
if (len > ) {
progressValues = new Array(len) //
progressContexts = new Array(len)
resolveContexts = new Array(len) //遍历每个对象
for ( ; i < len; ++i ) {
//如果是def,
if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) {
resolveValues[i].promise()
.done(updateFn(i, resolveContexts, resolveValues)) //每一个成功
.fail(deferred.reject)//直接挂入主def的失败通知函数,当某个子def失败时,调用主def的切换失败状态方法,执行主def的失败函数列表
.progress(updateFn(i, progressContexts, progressValues))
} else {
--remain //非def,直接标记成功,减1
}
}
} //都为非def,比如无参数,或者所有子队列全为非def,直接通知成功,进入成功函数列表
if (!remain) deferred.resolveWith(resolveContexts, resolveValues)
return deferred.promise()
} $.Deferred = Deferred
})(Zepto)

Promises/A+

  由于deferred是基于Promise规范,我们首先需要理清楚Promises/A+是什么。

  它的规范内容大致如下(此翻译内容引自这里) 

  • 一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)
  • 一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换
  • promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致
  • then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。

先用伪代码来实现其规范内容

      //普通的异步回调写法。
function fA(){
var a1,a2; //出现异常,调用其他方法
try{
fa1(a1);
fa2(a2);
}catch(e){
efa1(a1);
efa2(a2);
}
} function fa2(){
fB();//调用另一个和fA类似的异步回调
}
//下面采用Promise规范来改写 //初始化: 等待状态 pending
var Promise = {
status: pending, //状态
promise: function(o){
return {
done:done,
fail:fail
}
},
//必须申明的then方法
then:function(fulfilledFn,rejectedFn){
this.done(fulfilledFn);
this.fail(rejectedFn); //返回promise对象
return this;
}, //当状态切换fulfilled时执行
done: function(){ },
//当状态切换rejected时执行
fail:function(){ }, //切换为已完成状态
toFulfilled:function(){
this.status = 'fulfilled'
}, //切换为已拒绝状态
toRejected:function(){
this.status = 'rejected'
} } //将函数包装成Promise对象,并注册完成、拒绝链方法
//通过then
Promise.promise(fA).then(fa1,efa1).then(fa2,efa2);
//假定fb里还调用了另一个异步FB,
//之前fA的异步回调执行到fb方法
var PA = Promise.promise(fA).then(fa,efa).then(fb,efb);
//再挂上fB的异步回调
PA.then(fB).then(fb1,efb1).then(fb2,efb2);

Promise规范生命周期

Deferred用法

Deferred生命周期

Deferred设计

Zepto源码分析-deferred模块的更多相关文章

  1. 读Zepto源码之Deferred模块

    Deferred 模块也不是必备的模块,但是 ajax 模块中,要用到 promise 风格,必需引入 Deferred 模块.Deferred 也用到了上一篇文章<读Zepto源码之Callb ...

  2. zepto源码分析·ajax模块

    准备知识 在看ajax实现的时候,如果对ajax技术知识不是很懂的话,可以参看下ajax基础,以便读分析时不会那么迷糊 全局ajax事件 默认$.ajaxSettings设置中的global为true ...

  3. zepto源码分析·core模块

    准备说明 该模块定义了库的原型链结构,生成了Zepto变量,并将其以'Zepto'和'$'的名字注册到了window,然后开始了其它模块的拓展实现. 模块内部除了对选择器和zepto对象的实现,就是一 ...

  4. Zepto源码分析-ajax模块

    源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT l ...

  5. zepto源码分析·event模块

    准备知识 事件的本质就是发布/订阅模式,dom事件也不例外:先简单说明下发布/订阅模式,dom事件api和兼容性 发布/订阅模式 所谓发布/订阅模式,用一个形象的比喻就是买房的人订阅楼房消息,售楼处发 ...

  6. Zepto源码分析-event模块

    源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT l ...

  7. Zepto源码分析-form模块

    源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT l ...

  8. Zepto源码分析-callbacks模块

    // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT licens ...

  9. zepto源码分析系列

    如果你也开发移动端web,如果你也用zepto,应该值得你看看.有问题请留言. Zepto源码分析-架构 Zepto源码分析-zepto(DOM)模块 Zepto源码分析-callbacks模块 Ze ...

随机推荐

  1. .NET遇上Docker - 使用Docker Compose组织Ngnix和.NETCore运行

    本文工具准备: Docker for Windows Visual Studio 2015 与 Visual Studio Tools for Docker 或 Visual Studio 2017 ...

  2. 回到顶端的jquery

    现在的淘宝啊,京东啊,各种网站都有一个功能,有一个按钮,在页面最顶端的时候不会显示,当往下拉到一定的时候会出现.点击他会直接跳到页面的顶端.代码如下: html代码: <div id=" ...

  3. CSS选择器渲染效率

    1 浏览器如何识别你的选择器 首先我们需要清楚,浏览器是如何读取选择器,以识别样式,并将相应的样式附于对应的HTML元素,达到美化页面的效果.Chris Coyier曾在<Efficiently ...

  4. STM32之呼吸灯实验

    首先,我想引用一下在一片博文里 看到 的一段话,写的很详细, 首先来说,你要使用PWM模式你得先选择用那个定时器来输出PWM吧!除了TIM6.TIM7这两个普通的定时器无法输出PWM外,其余的定时器都 ...

  5. CF 690C3. Brain Network (hard) from Helvetic Coding Contest 2016 online mirror (teams, unrated)

    题目描述 Brain Network (hard) 这个问题就是给出一个不断加边的树,保证每一次加边之后都只有一个连通块(每一次连的点都是之前出现过的),问每一次加边之后树的直径. 算法 每一次增加一 ...

  6. CentOS 'mysql/mysql.h': No such file or directory

    需要安装mysql-devel # yum install mysql-devel

  7. XStream的使用

    一:功能 可以将JavaBean转换(序列化)成XMl 二:依赖jar包 xstream.jar xpp3_min.jar(xml pull parser)xml解析器 三:使用步骤 XStream ...

  8. C语言之函数和字符串

    二.函数: 2.1.函数的执行: 1.当我们每次进入一个函数的时候,原函数的栈底进行一个备份,之后将当前函数的栈底和栈顶指针分作同一个. 2.此时我们就可以说产生了一个新栈,产生新栈之后会在新栈中申请 ...

  9. 经验分享:如何用grep对PHP进行代码审计

    这是一个常见的误解- 企业需要购买复杂和昂贵的软件来发现应用程序中安全漏洞:而这些专门的软件应用程序,无论是黑盒或白盒,开源或商业,都能很快的发现安全漏洞. 事实是:所有这些专业的漏洞扫描工具都有其特 ...

  10. [进程通信] Linux进程间通信(IPC)

    简介 linux下进程间通信的几种主要手段: 1.      管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道 ...