Zepto源码分析-deferred模块

时间:2023-03-08 15:38:12
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规范生命周期

Zepto源码分析-deferred模块

Deferred用法

Zepto源码分析-deferred模块

Deferred生命周期

Zepto源码分析-deferred模块

Deferred设计

Zepto源码分析-deferred模块