源码注释
// 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:1, memory:1}), "resolved" ], [ "reject", "fail", $.Callbacks({once:1, memory:1}), "rejected" ], [ "notify", "progress", $.Callbacks({memory:1}) ] ], 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[1]](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[0] + "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[2],//$.Callback stateString = tuple[3]// 状态 如 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[1]] = list.add //切换的状态是resolve成功/reject失败 //添加首组方法做预处理,修改state的值,使成功或失败互斥,锁定progress回调列表, if (stateString) { list.add(function(){ state = stateString //i^1 ^异或运算符 0^1=1 1^1=0,成功或失败回调互斥,调用一方,禁用另一方 }, tuples[i^1][2].disable, tuples[2][2].lock) } //添加切换状态方法 resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith() deferred[tuple[0]] = function(){ deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments) return this } deferred[tuple[0] + "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 = 0, remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0, //子def计数 deferred = remain === 1 ? 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 > 1 ? 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 > 1) { 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设计