Zepto源码分析-deferred模块
源码注释
// 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模块的更多相关文章
- 读Zepto源码之Deferred模块
Deferred 模块也不是必备的模块,但是 ajax 模块中,要用到 promise 风格,必需引入 Deferred 模块.Deferred 也用到了上一篇文章<读Zepto源码之Callb ...
- zepto源码分析·ajax模块
准备知识 在看ajax实现的时候,如果对ajax技术知识不是很懂的话,可以参看下ajax基础,以便读分析时不会那么迷糊 全局ajax事件 默认$.ajaxSettings设置中的global为true ...
- zepto源码分析·core模块
准备说明 该模块定义了库的原型链结构,生成了Zepto变量,并将其以'Zepto'和'$'的名字注册到了window,然后开始了其它模块的拓展实现. 模块内部除了对选择器和zepto对象的实现,就是一 ...
- Zepto源码分析-ajax模块
源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT l ...
- zepto源码分析·event模块
准备知识 事件的本质就是发布/订阅模式,dom事件也不例外:先简单说明下发布/订阅模式,dom事件api和兼容性 发布/订阅模式 所谓发布/订阅模式,用一个形象的比喻就是买房的人订阅楼房消息,售楼处发 ...
- Zepto源码分析-event模块
源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT l ...
- Zepto源码分析-form模块
源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT l ...
- Zepto源码分析-callbacks模块
// Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT licens ...
- zepto源码分析系列
如果你也开发移动端web,如果你也用zepto,应该值得你看看.有问题请留言. Zepto源码分析-架构 Zepto源码分析-zepto(DOM)模块 Zepto源码分析-callbacks模块 Ze ...
随机推荐
- python黑魔法之metaclass
最近了解了一下python的metaclass,在学习的过程中,把自己对metaclass的理解写出来和大家分享. 首先, metaclass 中文叫元类,这个元类怎么来理解呢.我们知道,在Pytho ...
- require.js学习笔记
使用require.js的好处? 1 有效的防止命名冲突(可以将变量封装在模块内,通过暴露出的接口解决命名冲突) 2 解决不同JS文件中的依赖 3 可以让我们的代码以模块化的方式组织 官方网站http ...
- 点击按钮,缩放图片(img.width、img.style.width、img.offsetWidth)
前几天在慕课网上看到一个关于图片缩放的教学视频,因为当时对老师使用img.width,而不是使用img.style.width而感到奇怪,所以周末得空了,想来自己试着写出来,相关视频网址如下:http ...
- day01课程回顾,数据类型
Day01 Python的分类 Cpython:代码àc字节码->机器码 一行一行的编译执行 Pypy: 代码àc字节码->机器码 全部转换完再执行 其他python 代码- ...
- 【转】为什么delete以后指针还能被赋值
首先,系统知道哪一部分堆的线性空间被占掉了,new就是起这个作用,仅仅是声明一下(可能多了一个功能),因为堆的空间不一定是直接从系统调用获得的,堆的空间是这样管理的:程序先伸请一个大的堆空间,这个时候 ...
- xmlplus 组件设计系列之九 - 树(Tree)
树形组件是一种具有层级结构的组件,广泛应用于各种场景.本章会实现一个简单的树形组件,尽管功能有限,但你可以通过扩展它来实现自己所需要的树形组件. 数据源 树形组件的数据源可以是 JSON 格式的数据对 ...
- for循环之初学者N多算法小练习
for循环之初学者N多算法小练习 显示1到100的数,每行显示5个. for (int i=1;i<=100;i++){ if (i%5==0){ System.out. ...
- C#基础知识-流程控制的应用(四)
流程控制我们在编程中运用到的地方非常的多,在上篇中仅仅只是简单的介绍每一种的使用,并没有运用到实例中,很难去理解它真正的作用.下面我们将实际的运用流程控制的代码写一些实例相关的程序,加深对流程控制的理 ...
- 【2017-05-05】timer控件、三级联动、帐号激活权限设置
一.Timer控件 Timer实际就是一个线程控件. 属性:Enabled 是否被启用 Interval 多长时间执行一次控件中的代码 事件: Tick 事件中放要执行的代码. ...
- 设计模式--MVC(C++版)
MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式.这种模式用于应用程序的分层开发. Model(模型)-是应用程序中用于处理应用程序数据逻辑的部分.通常模型对象 ...