源码注释

  

//     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. Spring+Redis(keyspace notification)实现定时任务(订单过期自动关闭)

    1.起因 最近公司项目要做订单超期未支付需自动关闭,首先想到的是用spring的定时器(@Schedule),结果领导举各种例子说会影响性能,只能作罢.后来想能不能基于redis实现, 学习(baid ...

  2. 和我一步步部署 kubernetes 集群

    和我一步步部署 kubernetes 集群 本系列文档介绍使用二进制部署最新 kubernetes v1.6.1 集群的所有步骤,而不是使用 kubeadm 等自动化方式来部署集群: 在部署的过程中, ...

  3. grid实例(Asp.net)

    <link href="../../js/jqGrid/css/ui.jqgrid.css" rel="stylesheet" type="te ...

  4. angular中路由的实现(针对新手)

    最近搜了一下网上的教程,看完总感觉有点糊涂,对于新手来说可能云里雾里,所以写一个最简单的路由来给大家做个指引. 首先当然需要准备angular,下载地址:https://angular.io/ 现在a ...

  5. Spring + Mybatis 项目实现动态切换数据源

    项目背景:项目开发中数据库使用了读写分离,所有查询语句走从库,除此之外走主库. 最简单的办法其实就是建两个包,把之前数据源那一套配置copy一份,指向另外的包,但是这样扩展很有限,所有采用下面的办法. ...

  6. linux下安装node

    经过一番的折腾终于在linux上安装了node,记录下来以免忘记 1.下载node 去官网下载最新的linux版本下对应node.js,node-v6.10.2-linux-x64.tar.gz 2. ...

  7. gif-drawable的使用及详解

    下载gif-drawable包和Demo的链接:http://pan.baidu.com/s/1eQxVKRo 本帖原创,转载的朋友请注明转载地址>:http://www.cnblogs.com ...

  8. 计算机程序的思维逻辑 (82) - 理解ThreadLocal

    本节,我们来探讨一个特殊的概念,线程本地变量,在Java中的实现是类ThreadLocal,它是什么?有什么用?实现原理是什么?让我们接下来逐步探讨. 基本概念和用法 线程本地变量是说,每个线程都有同 ...

  9. Websphere(was)与Weblogic部署EJB的注意项

    复杂的故事简单说,复杂的问题简单做. EJB容器 简介 本节讲解EJB项目在Weblogic和Was上的部署需要注意设置的一些内容.不同的中间件对EJB支持方式不一样,所以配置的原理也略有差异. 关键 ...

  10. Less和Sass的使用

    [Less中的变量] 1.声明变量:@变量名:变量值;  使用变量:@变量名 @length:100px; @color:yellow; @opa:0.5; >>>Less中变量的类 ...