技术一般水平有限,有什么错的地方,望大家指正。
ES6已经实现了延迟对象Promise,但是本文主要是对JQ里面的延迟对象,套路其实都是差不多的
jQuery 1.5中介绍了 Deferred 延迟对象,它是通过调用 jQuery.Deferred() 方法来创建的可链接的实用对象。它可注册多个回调函数到回调列表,调用回调列表并且传递异步或同步功能的成功或失败的状态。 延迟对象是可链接的,类似于一个 jQuery 对象可链接的方式,区别于它有自己的方法。
在创建一个 Deferred 对象之后,您可以使用以下任何方法,直接链接到通过调用一个或多个的方法创建或保存的对象。它是jQuery出的,为了解决回调嵌套,方便开发者的一种函数
方法列表
| 函数 | 说明 |
|---|---|
| $.Deferred() | 用来返回一个链式实用对象方法来注册多个回调,并且调用回调队列,传递任何同步或异步功能成功或失败的状态。 |
| deferred.always() | 用于当Deferred(延迟)对象被受理或被拒绝时,调用添加的处理程序 |
| deferred.done() | 用于当Deferred(延迟)对象被受理时,调用添加的处理程序 |
| deferred.fail() | 用于当 Deferred (延迟)对象被拒绝时,调用添加的处理程序 |
| deferred.progress() | 用于当Deferred(延迟)对象生成进度通知时,调用添加处理程序 |
| 用来确定 Deferred 对象是否已被拒绝 | |
| 用来确定 Deferred 对象是否已被拒绝 | |
| deferred.notify() | 用于传递参数给进行中的回调 |
| deferred.notifyWith() | 用于传递参数,上下文对象给进行中的回调 |
| deferred.reject() | 用于拒绝延迟对象,并传递参数给失败回调函数 |
| deferred.rejectWith() | 用于拒绝延迟对象,并传递参数和上下文对象给失败回调函数 |
| deferred.resolve() | 用于解决延迟对象,并并传递参数给doneCallbacks 回调函数 |
| deferred.resolveWith() | 用于解决延迟对象,并并传递参数和上下文对象给doneCallbacks 回调函数 |
| deferred.state() | 用于确定一个Deferred(延迟)对象的当前状态 |
| deferred.pipe() | 用于过滤状态或通过函数返回的延迟对象的值 |
| deferred.then() | 用于当Deferred(延迟)对象被解决,拒绝或仍在进行中时,调用添加处理程序。 |
| deferred.catch() | 用于当Deferred对象被拒绝(reject)时,调用添加的处理程序。 |
| deferred.promise() | 用于返回 Deferred(延迟)的 Promise 对象 |
| .promise() | 用于返回一个 Promise 对象,观察某种类型被绑定到集合的所有行动,是否已被加入到队列中。 |
延长对象说明
$.Deferred()
$.Deferred() 是一个构造函数,用来返回一个链式实用对象方法来注册多个回调,并且调用回调队列,传递任何同步或异步功能成功或失败的状态。
提示:
- $.Deferred() 构造函数创建一个新的 Deferred(延迟)对象, jQuery.Deferred可传递一个可选的函数,该函数在构造方法返回之前被调用并传递一个新的 Deferred 对象作为函数的第一个参数。例如被调用的函数可以使用 deferred.then()来附加回调函数。
- 一个 Deferred 对象开始于挂起状态。任何使用 deferred.then(), deferred.always(), deferred.done(), 或者 deferred.fail() 添加到这个对象的回调函数都是排队等待执行的。调用 deferred.resolve() 或 eferred.resolveWith() 转换延迟到解决状态后立即执行设置的doneCallbacks 。 调用 deferred.reject() 或 deferred.rejectWith() 转换延迟到拒绝状态后立即执行设置的 failCallbacks 。一旦对象已经进入了解决或拒绝状态,它保持该状态。回调仍然可以添加到已解决或已拒绝的 Deferred 对象——它们会立即执行。
AJAX操作的链式写法
jQuery的ajax操作的传统写法
$.ajax({
url: "test.html",
type:"get",
dataType:"json",
success: function(){\
alert("哈哈,成功了!");\
},
error:function(){\
alert("出错啦!");\
},
complete:function(XMLHttpRequest, textStatus){
}
});
在上面的代码中,$.ajax()接受一个对象参数,这个对象包含两个方法:success方法指定操作成功后的回调函数,error方法指定操作失败后的回调函数,(complete:请求完成后调用的回调函数(请求成功或失败时均调用)。参数:XMLHttpRequest对象和一个描述成功请求类型的字符串)。
$.ajax()操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR对象,你没法进行链式操作;如果高于1.5.0版本,返回的是deferred对象,可以进行链式操作。
$.ajax("test.html")
.done(function(){ alert("嗯,成功了!"); })
.fail(function(){ alert("哦,出错啦!"); });
看到,done()相当于success方法,fail()相当于error方法。而且可以采用链式写法以后,代码的可读性大大提高。
同一操作多个回调函数
deferred对象的一大好处,就是它允许你自由添加多个回调函数。 还是以上面的代码为例,如果ajax操作成功后,除了原来的回调函数,我还想再运行一个回调函数,怎么办? 很简单,直接把它加在后面就行了。
$.ajax("test.html")
.done(function(){ alert("嗯,成功了!");} )
.fail(function(){ alert("哦,出错啦!"); } )
.done(function(){ alert("我要调第二个回调函数了!");} );
回调函数可以添加任意多个,它们按照添加顺序执行
deferred对象的另一大好处,就是它允许你为多个事件指定一个回调函数,它用到了一个新的方法$.when()的参数只能是deferred对象,
$.when($.ajax("test1.html"), $.ajax("test2.html"))
.done(function(){ alert("嗯,成功了!"); })
.fail(function(){ alert("哦,出错啦!"); });
这段代码的意思是,先执行两个操作.ajax("test2.html"),如果都成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。
deferred.resolve()方法和deferred.reject()方法
jQuery规定,deferred对象有三种执行状态----未完成,已完成和已失败。如果执行状态是"已完成"(resolved),deferred对象立刻调用done()方法指定的回调函数;如果执行状态是"已失败",调用fail()方法指定的回调函数;如果执行状态是"未完成",则继续等待,或者调用progress()方法指定的回调函数(jQuery1.7版本添加)。
前面部分的ajax操作时,deferred对象会根据返回结果,自动改变自身的执行状态;类似的,还存在一个deferred.reject()方法,作用是将dtd对象的执行状态从"未完成"改为"已失败",从而触发fail()方法
JQ为我们实现了延迟对象的功能,我们一般称为Deferred或者Promise,基本上是一个东西,确切的说Promise是从Deferred中派生的一个子类。
我们在使用的时候首先就是创建一个延迟对象:var deferred = new $.Deferred()。
延迟对象deferred有三种状态分别为pending,resolved,rejected,我们可以通过对deferred对象使用state方法来查看此时的状态:deferred.state()。
deferred在创建出来之后他的状态为pending,调用resolve方法之后:deferred.resolve()它的状态就会变为resolved然后会执行deferred.done()里面的函数,deferred调用reject方法之后:deferred.reject()它的状体就会变为rejected然后会执行deferred.fail()里面的方法,并且deferred对象在从pending变为resolved或者rejected之后就不会再发生任何变化,这也就是我们上面的代码为什么只能在第一次点击之后的文字是红的的原因。
deferred.always()
形式:deferred.always( alwaysCallback [, alwaysCallback] );
当Deferred(延迟)对象被处理或被拒绝时,调用通过deferred.always() 添加的处理程序。
提示:参数可以是一个函数或一个函数数组。由于 deferred.always() 返回的是一个 Deferred 对象,所以可以连接其他的延迟对象方法(这里是指可以继续调用Deferred对象的方法),包括额外的 .always 方法。 当 Deferred 对象得到解决或被拒绝时,通过deferred.always() 添加的回调函数按它们被添加时的顺序执行,并且可以作为参数传递给如下的方法使用:resolve , reject , resolveWith 或 rejectWith。
注意: deferred.always()方法接收了Deferred对象.resolve()或.reject()使用的参数, 这往往是非常不同的。 为此, 最好的只用它的行为,而不去检查他的参数。 在大多数情况下, 使用显式 .done() 或 .fail()处理程序,因为他们的参数都是已知的。
返回值 deferred.always()返回Deferred(延迟)对象
deferred.done()
当Deferred(延迟)对象被受理时,调用通过延迟对象函数deferred.done()添加的处理程序。
提示:该方法接受一个或者多个参数。deferred.done() 返回的是一个 Deferred 对象, 可以连接其他的延迟对象方法(继续调用Deferred 对象的方法),包括额外的 .done() 方法。当Deferred 对象得到解决时,通过deferred.done()添加的回调函数按它们被添加时的顺序执行,并且可以作为参数传递给如下的方法使用:resolve,resolveWith。
语法 deferred.done( doneCallbacks [, doneCallbacks ] )
$("p").append(" 1 ");
}
function fn2() {
$("p").append(" 2 ");
}
function fn3(n) {
$("p").append(n + " 3 " + n);
}
var dfd = $.Deferred();//创建一个延迟对象
//添加dfd被解决时需要的被调用的处理函数
dfd
.done( [fn1, fn2], fn3, [fn2, fn1] )// 连接一个函数或者函数数组
.done(function(n) {//可以连接其他的方法
$("p").append(n + " we're done.");
});
//当点击按钮时修改延迟对象的状态为已解决
//此时调用通过deferred.done()添加的延迟对象被受理后的处理函数
$("button").bind("click", function() {
dfd.resolve("and");//输出:1 2 and 3 and 2 1 and we're done.
});
eferred.fail()
当 Deferred (延迟)对象被拒绝时,调用通过deferred.fail()添加的处理程序。
提示:该方法接受一个或者多个参数。 deferred.fail() 返回的是一个 Deferred 对象, 可以连接其他的延迟对象方法(继续调用其他Deferred 对象的方法),包括额外的 .fail() 方法。当 Deferred 对象被拒绝时,通过deferred.fail()添加的回调函数 按它们被添加时的顺序执行,并且可以作为参数传递给如下的方法使用:deferred.resolve() 或 deferred.rejectWith()。
语法 deferred.fail( failCallbacks [, failCallbacks ] )
deferred.progress()
deferred.progress() 函数当Deferred(延迟)对象生成进度通知时,调用添加处理程序。
注意:当通过调用 notify 或 notifyWith 使延迟对象产生进度通知时,progressCallbacks 就会被调用。 由于 deferred.progress()返回的是延迟对象,所以其它延迟对象方法可以链接到该对象上(链式调用)。当延迟对象被 resolved 或 rejected 时, 进度回调函数将不再被调用,但是当Deferred (延迟)进入resolved(解决) 或 rejected(拒绝)状态后,新添加任何的progressCallbacks将立即执行,使用的参数被传递给.notify() 或 notifyWith()调用
语法 deferred.progress( progressCallbacks[,progressCallbacks] )
deferred.isRejected()
deferred.isRejected() 函数用来确定 Deferred 对象是否已被拒绝。
从jQuery1.7开始已经过时,请使用 deferred.state() 代替。
注意:
如果 Deferred(延迟)对象是在被拒绝的状态,则返回 true。这意味着 deferred.reject() 或者deferred.rejectWith() 已经在对象上被调用过,并且 failCallbacks 已经被调用过(或者正处于被调用的阶段)。
Deferred(延迟)对象可以有三种状态:挂起(pending),解决(resolved),或拒绝(rejected);使用 deferred.isResolved() 来判断延迟对象是否在解决状态。
语法
deferred.isRejected()
deferred.isResolved()
deferred.isResolved() 函数用来确定 Deferred 对象是否已被解决。
从jQuery1.7开始已经过时,请使用 deferred.state() 代替。
注意:
如果 Deferred(延迟)对象是在被解决的状态,则返回 true。这意味着 deferred.resolve() 或者 deferred.resolveWith() 已经在对象上被调用过,并且 doneCallbacks 已经被调用过(或者正处于被调用的阶段)。
Deferred(延迟)对象可以有三种状态:挂起(pending),解决(resolved),或拒绝(rejected);使用 deferred.isRejected() 来判断延迟对象是否在拒绝状态。
语法
deferred.isResolved()
deferred.notify()
deferred.notify() 函数用于定一个参数,传递给正在调用的延迟对象上的回调函数 ( progressCallbacks )。
注意: 通常,只有延迟对象(Deferred)的创建者才能调用此方法。 你可以通过调用 deferred.promise() 返回一个受限的 Promise 对象,来阻止其它代码改变延迟对象的状态或报告它的状态。 当 deferred.notify 被访问时, 任何 progressCallbacks 可以通过访问 deferred.then 或者 deferred.progress 来添加。回调依照他们添加时的顺序执行。 通过来自.notify()的传递参数给正在调用的延迟对象上的回调函数,当迟延对象已经被 resolved 或被 rejected 之后,再调用任何 .notify() (或者添加 progressCallbacks) 都会 被忽略
语法 deferred.notify( args )
deferred.notifyWith()
deferred.notifyWith() 函数用于给定上下文和参数,传递给正在调用的延迟对象上进行的回调函数( progressCallbacks )。
注意: 通常,只有延迟对象(Deferred)的创建者才能调用此方法。 你可以通过调用 deferred.promise() 返回一个受限的 Promise 对象,来阻止其它代码改变延迟对象的状态或报告它的状态。 当 deferred.notifyWith 被调用时, 任何 progressCallbacks 可以通过访问 deferred.then 或者 deferred.progress 来添加。回调 依照他们添加时的顺序执行。 通过 .notifyWith() 传递参数给每个回调函数,当迟延对象已经被 resolved 或被 rejected 之后,再调用任何 .notifyWith() (或者添加 progressCallbacks) 都会被忽略。
语法 deferred.notifyWith( context [, args ] )
| 参数 | 说明 |
|---|---|
| context | Object类型 作为this对象,传递给进行中的回调(progressCallbacks) |
| args | Array类型 传递一个可选的参数给进行中的回调(progressCallbacks) |
deferred.reject()
deferred.reject() 函数用于拒绝延迟对象,并根据给定的参数调用任何 failCallbacks 回调函数。
注意:
通常只有延迟对象的创建者才可以调用该方法。你可以通过调用 deferred.promise(),返回一个受限的 Promise 对象,来阻止其它代码改变延迟对象的状态或报告它的状态。
当延迟对象被 rejected 时,任何通过 deferred.then 或 deferred.fail 添加的 failCallbacks,都会被调用。回调函数按它们被添加时的顺序执行。传递给 deferred.reject() 的 args 参数,会传给每个回调函数。当延迟对象进入 rejected 状态后,任何 failCallbacks 被添加时,就会被立刻执行,并带上传入给 .reject() 的参数。
语法
deferred.reject( args )
`// 拒绝deferred对象,调用所有failCallback函数`
`// failCallback可通过then方法中第二个参数设置,也可通过dtd.fail( failCallback )添加`
`// 参数将传递给failCallback。参数可选`
`// 只有deferred对象的创建者才可以调用的方法`
`// failCallback中this为deferred或promise对象`
`// failCallback只接收一个参数`
deferred.rejectWith()
deferred.rejectWith() 函数用于拒绝延迟对象,并根据给定的 context 和 args 参数调用任何 failCallbacks 回调函数。
注意:
通常只有延迟对象的创建者才可以调用该方法。你可以通过调用 deferred.promise(),返回一个受限的 Promise 对象,来阻止其它代码改变延迟对象的状态或报告它的状态。
当延迟对象被 rejected 时,任何通过 deferred.then 或 deferred.fail 添加的 failCallbacks,都会被调用。回调函数按它们被添加时的顺序执行。传递给 deferred.reject() 的 args 参数, 会传给每个回调函数。当延迟对象进入 rejected 状态后,任何 failCallbacks 被添加时,就会被立刻执行,并带上传入给 .reject() 的参数。
语法
deferred.rejectWith( context [, args ] )
`// 解决deferred对象,调用所有failCallback函数`
`// 参数:第一个参数为上下文即this对象,failCallback的this将被修改;第二个参数为数组`
`// failCallback中this为调用rejectWith方法的上下文`
`// failCallback接收参数个数为该方法第二个参数数组的长度`
`// 与resolve方法的区别在于,将改变failCallback函数的this指向`
deferred.resolve()
deferred.resolve() 函数用于解决Deferred(延迟)对象,并根据给定的args参数给完成回调函数doneCallbacks 。
注意:
通常只有延迟对象的创建者才可以调用该方法。你可以通过调用 deferred.promise(),返回一个受限的 Promise 对象,来阻止其它代码改变延迟对象的状态或报告它的状态。
当延迟对象被 resolved 时,任何通过 deferred.then 或 deferred.done 添加的 doneCallbacks,都会被调用。回调函数按它们被添加时的顺序执行。传递给 deferred.resolve() 的 args 参数, 会传给每个回调函数。当延迟对象进入 resolved 状态后,任何 doneCallbacks 被添加时,就会被立刻执行,并带上传入给 .resolve() 的参数。
语法
deferred.resolve( args )
`// 解决deferred对象,调用所有doneCallback函数`
`// doneCallback可通过then方法中第一个参数设置,也可通过dtd.done( doneCallback )添加`
`// 参数将传递给doneCallback。参数可选`
`// 只有deferred对象的创建者才可以调用的方法`
`// doneCallback中this为deferred或promise对象`
`// doneCallback只接收一个参数`
deferred.resolveWith()
deferred.resolveWith() 函数用于解决Deferred(延迟)对象,并根据给定的 context 和 args 参数给完成回调函数doneCallbacks 。
注意:
通常只有延迟对象的创建者才可以调用该方法。你可以通过调用 deferred.promise(),返回一个受限的 Promise 对象,来阻止其它代码改变延迟对象的状态或报告它的状态。 当延迟对象被 resolved 时,任何通过 deferred.then 或 deferred.done 添加的 doneCallbacks,都会被调用。回调函数按它们被添加时的顺序执行。传递给 deferred.resolve() 的 args 参数, 会传给每个回调函数。当延迟对象进入 resolved 状态后,任何 doneCallbacks 被添加时,就会被立刻执行,并带上传入给 .resolve() 的参数。 语法 deferred.resolveWith( context [, args ] )
`// 解决deferred对象,调用所有doneCallback函数`
`// 参数:第一个参数为上下文即this对象,doneCallback的this将被修改;第二个参数为数组`
`// doneCallback中this为调用resolveWith方法的上下文`
`// doneCallback接收参数个数为该方法第二个参数数组的长度`
`// 与resolve方法的区别在于,将改变doneCallback函数的this指向`
deferred.state()
deferred.state() 函数用于确定一个Deferred(延迟)对象的当前状态。
主要是用于调试,例如,在准备拒绝(reject)一个延迟对象前,判断它是否已经处于 resolved 状态。
注意:
deferred.state() 方法返回一个字符串,代表Deferred(延迟)对象的当前状态。Deferred 对象可以在三种状态之一:
“pending” : Deferred 对象是尚未完成状态 。
“resolved” : Deferred 对象是在解决状态,这意味着对象的 deferred.resolve() 或者
deferred.resolveWith()已被调用并且 doneCallbacks 已被调用(或在被调用的过程中)
“rejected” : Deferred 对象是在被拒绝的状态,这意味着对象的 deferred.reject() 或者
deferred.rejectWith() 已被调用并且 failCallbacks 已被调用(或在被调用的过程中) 。
语法
deferred.state()
//返回deferred对象当前状态,pending / resolved / rejected`
//不接受任何参数
deferred.then()
deferred.then() 函数当Deferred(延迟)对象被解决/拒绝或仍在进行中时,调用添加处理程序。
注意:
如果没有这种类型的回调是需要的,参数可以为 null 。或者使用.done(),.fail()或者 .progress()设置只有一种未经过滤的状态或值的回调类型。 从jQuery 1.8开始, 方法返回一个新的 promise ,可以通过一个函数过滤延迟对象的状态和值,用来替换现在过时的deferred.pipe() 方法。 回调是依照他们被添加时的顺序执行的,由于 deferred.then 返回 Promise 对象,可以链接其它的 Promise 对象,包括附加的 .then() 方法。 doneFilter 和 failFilter函数过滤原延迟对象的解决/拒绝的状态和值。 progressFilter 函数过滤任何调用原有的延迟对象的notify 和 notifyWith的方法。 这些过滤器函数可以返回一个新的值传递给的 Promise 对象的.done() 或 .fail() 回调,或他们可以返回另一个观察的对象(延迟对象,Promise 对象等)传递给它的解决/拒绝的状态和值,Promise 对象的回调。 如果过滤函数是空,或没有指定,promise(承诺)将得到与原来值相同解决(resolved)或拒绝(rejected)。
语法 deferred.then( doneFilter [, failFilter ] [, progressFilter ] ) 参数说明
| 参数 | 说明 |
|---|---|
| doneFilter | Function类型 可选 当Deferred(延迟)对象得到解决时被调用的一个函数 |
| failFilter | Function类型 可选 当Deferred(延迟)对象得到拒绝时被调用的一个函数 |
| progressFilter | Function类型 可选 当Deferred(延迟)对象生成进度通知时被调用的一个函数调用 |
| doneCallbacks | Function类型 当Deferred(延迟)对象得到解决时被调用的一个函数或函数数组 |
| failCallbacks | Function类型 当Deferred(延迟)对象得到拒绝时被调用的一个函数或函数数组 |
| progressCallbacks | Function类型 当Deferred(延迟)对象生成进度通知时被调用的一个函数或函数数组 |
deferred.promise()
deferred.promise() 函数返回 Deferred(延迟)的 Promise 对象。
注意:
方法允许一个异步函数阻止那些干涉其内部请求的进度(progress)或状态(status)的其它代码。
Promise 对象只包含 deferred 对象的一组方法,包括:done(),then(),fail(),isResolved(), isRejected(), always(), 这些方法只能观察一个 deferred 的状态;不包括任何用于改变状态的延迟方法(resolve, reject, notify, resolveWith, rejectWith, 和 notifyWith),因此Promise 对象无法更改 deferred 对象的内在状态
deferred.promise()也可以接受一个 target 参数,此时传入的 target 将被赋予 Promise 的方法,并作为结果返回,而不是创建一个新对象(这个方法可以用于在已经存在的对象上绑定 Promise 行为的情况)。
语法
deferred.promise( [target ] )
// 目的: 防止其他代码干涉其内部进度和状态
// 返回新的promise对象,包含可以执行的方法( done, fail, then, always, progress, state, promise ),
// 不包含修改Deferred状态的方法( resolve, reject, notify, resolveWith, rejectWith, nodifyWith )
// 需返回deferred对象时,建议返回deferred.promise()
DEferred源码
/**
Deferred 委托人对象,对委托人管理
*/
jQuery.extend({
/**
创建一个Deferred对象,"延迟"到未来某个点再执行。我们称之为Deferred,也就是委托人,回调函数就是观察者
方式:在函数内部,创建一个deferred,为deferred添加一些方法,通过func.call(deferred,deferred)方式把这个deferred对象插入到func函数参数中
目的:处理耗时操作的问题(回调函数),为了对这些操作更好的控制,提供了统一的编程接口(API)。
* @param {Function} func 回调函数 (使用call方式,将deferred代入到函数的参数中)
* @return {Object} deferred 延迟对象
*/
Deferred: function (func) {
/**
* 创建一个数据元组集
* 每个元组分别包含一些与当前委托人(deferred)相关的信息:
*
Deferred自身则围绕这三个对象进行更高层次的抽象
通知(触发回调函数列表执行(函数名))
观察者(添加回调函数(函数名))
观察者对象(jQuery.Callbacks对象)
委托人状态(第三组数据除外)
* 总体而言,三个元组会有对应的三个callbacklist对应于doneList, failList, processList
* resolve 委托人接到通知,告诉观察者执行“已完成”操作(deferred对象的执行状态从“未完成”变为“已完成”,触发done(侦听器))
* reject 委托人接到通知,告诉观察者执行“已拒绝”操作,deferred对象的执行状态,从“未完成”变为“已失败”,触发fail(侦听器)
* notify 委托人接到通知,告诉观察者执行“还在进行中,或者未完成”操作
* done 成功(回调函数)
* fail 失败(回调函数)
* progress 未完成
* resolved 完成状态
* rejected 失败状态
*/
var tuples = [ // action 执行状态, add listener 添加侦听器(事件处理函数),listener list 侦听器列表(事件处理函数列表),final state 最终状态 ["resolve", "done", jQuery.Callbacks("once memory"), "resolved"],
["reject", "fail", jQuery.Callbacks("once memory"), "rejected"],
["notify", "progress", jQuery.Callbacks("memory")]
],
// 委托人三种状态,(deferred的状态)分为三种:pending(挂起状态) resolved(完成状态) rejected(失败状态)
state = "pending",
/**
* 创建一个promise对象 也就是一个受限制的委托人,只能执行观察者,不能通知观察者
* 作用:1.在初始化deferred对象时,promise对象里的方法会被extend到deferred中去,作为引用
* 2.保护deferred对象,使其无法改变deferred对象的执行状态,要想改变执行状态,只能操作原来的deferred对象,仅支持done,fail,progress方法
*/
promise = {
/**
返回委托人状态
* return {String} 返回委托人状态(外面只读)
*/
state: function () {
return state;
},
/**
不管最后是resolve还是reject,都会触发fn
不管委托人发出什么样的通知都会去执行观察者
同时在doneList和failList的list里添加回调函数(引用),不管deferred对象的执行状态成功还是失败,回调函数都会被执行
* return {Object} 返回当前委托人
*/
always: function () {
deferred.done(arguments).fail(arguments);
return this;
},
/**
接受三个参数,对应3种状态的回调函数,这三个回调函数,必须返回deferred对象
* @param {Function} fnDone 成功的观察者,done()方法的回调函数
* @param {Function} fnFail 失败的观察者,fail()方法的回调函数
* @param {Function} fnProgress 打酱油的观察者,progress()方法的回调函数
* @return {Object} 返回一个受到限制的委托人
*/
then: function ( /* fnDone, fnFail, fnProgress */) {
// 声明一个变量,并把参数引用赋值给funs
var fns = arguments;
// newDefer 创建一个新的委托人
return jQuery.Deferred(function (newDefer) {
jQuery.each(tuples, function (i, tuple) {
// i 当前tuples的下标
// tuple 当前tuples[i]的值
var action = tuple[0],// 当前委托人状态 tuple[0]对应三种最终状态resolve,reject,notify
fn = jQuery.isFunction(fns[i]) && fns[i];// 当前观察者
/**
老版的委托人发出的通知绑定不同的观察者
* deferred指老版的委托人
* tuple[1]指[done | fail | progress]
* fn 老版委托人的观察者
* 分别为deferred的三个callbacklist(状态)添加回调函数,根据fn的是否是函数,分为两种情况:
* 1. fn不是函数,(例如:undefined和null),直接链接到newDefer的通知([resolve | reject | notify]方法),也就是说新的委托人newDefer的通知依赖外层调用者(老版委托人)deferred执行的观察者(done,fail,progress)
* 2. fn是函数,根据返回值(ret)是否为委托人deferred对象,分为两种情况:
* a) 返回值是deferred对象,那么将新委托人newDefer的通知([resolve | reject | notify]方法)添加到ret对象对应的观察者上,也就说newDefer的执行依赖ret的状态
* b) 返回值不是deferred对象,那么将ret作为newDefer的参数,判断this是否为老版委托人deferred,是则将newDefer作为上下文环境,不是将this作为上下文执行环境,然后执行对应的回调函数列表,Ps:此时newDefer的执行(通知)依赖外层的调用者deferred的状态(观察者)。
*/
//相当于deferred.done,deferred.fail,deferred.progess三种状态;
deferred[tuple[1]](function () {
// 执行老版委托人传递的参数(回调函数)
var returned = fn && fn.apply(this, arguments);
if (returned && jQuery.isFunction(returned.promise)) {
returned.promise()
.done(newDefer.resolve)
.fail(newDefer.reject)
.progress(newDefer.notify);
} else {
newDefer[action + "With"](this === promise ? newDefer.promise() : this, fn ? [returned] : arguments);
}
});
});
// 清空变量,内存回收
fns = null;
}).promise();//返回一个受到限制的委托人
},
/**
为委托人加入一些限制(为deferred添加一个promise方法)
* @param {Object} 委托人或空值
* @return {Object} 假如对象存在,将promise添加到这个对象中,不存在返回promise对象
*/
promise: function (obj) {
return obj != null ? jQuery.extend(obj, promise) : promise;
}
},
// 声明变量 创建一个委托人
deferred = {};
// 添加一个方法名pipe与then相同,一般使用pipe进行filter操作
promise.pipe = promise.then;
jQuery.each(tuples, function (i, tuple) {
//声明一个局部变量,将观察器对象引用给list,3个$.Callback()的实例
var list = tuple[2],
stateString = tuple[3]; //委托人的最终状态 resolved,rejected
// 为观察者对象添加函数(回调函数)
// promise[done|fail|progress]=list.add;
// 这三个方法分别引用三个不同的观察者对象的add方法,往观察者对象回调函数列表list添加回调函数[done|fail|progress],互不打扰
promise[tuple[1]] = list.add;
/*
观察者对象
* add 添加回调函数
* fire 调用fireWidth
* fireWidth 去执行回调函数
*/
// 检测stateString是否有值,progress没有值,所以预先向[done|fail]添加一些定义好的回调函数
// 定义好的回调函数:1.修改委托人的状态;2.禁用相反的观察者对象;3.加锁(挂起)正在进行的观察者对象
if (stateString) {
list.add(function () {
// state = [ resolved | rejected ]
// 修改最新的委托人状态
state = stateString;
//禁用对立的那条队列
//注意 0^1 = 1 1^1 = 0
//即是成功的时候,把失败那条队列禁用
//即是成功的时候,把成功那条队列禁用
// [ reject_list | resolve_list ].disable; progress_list.lock
}, tuples[i ^ 1][2].disable, tuples[2][2].lock);
}
// 将委托人的通知和观察者对象的执行回调函数(观察者函数) 联系起来
// deferred[ resolve | reject | notify ]
deferred[tuple[0]] = function () {
deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments);
return this;
};
deferred[tuple[0] + "With"] = list.fireWith;
});
// 为委托人添加一个被限制的委托人对象 (将promise对象extend到deferred中)
promise.promise(deferred);
// 检测func是否存在,存在将生成的委托人对象作为这个函数的默认参数,以及将this指向这个委托人
if (func) {
func.call(deferred, deferred);
}
// 返回委托人对象
return deferred;
},
//注意到$.when是多任务的
//当一个任务失败的时候,代表整个都失败了。
//任务是Deferred实例,成为异步任务
//任务是普通function时,成为同步任务
when: function (subordinate /* , ..., subordinateN */) {
var i = 0,
//arguments是多个任务
resolveValues = core_slice.call(arguments),
length = resolveValues.length,
//还没完成的异步任务数
//subordinate && jQuery.isFunction(subordinate.promise)判断subordinate是不是Deferred的实例对象
remaining = length !== 1 || (subordinate && jQuery.isFunction(subordinate.promise)) ? length : 0,
//只有一个异步任务的时候
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
//用于更新 成功|处理 中两个状态
//这里不考虑失败的状态是因为:
//当一个任务失败的时候,代表整个都失败了。
updateFunc = function (i, contexts, values) {
return function (value) {
contexts[i] = this;
values[i] = arguments.length > 1 ? core_slice.call(arguments) : value;
//处理中,派发正在处理事件
if (values === progressValues) {
deferred.notifyWith(contexts, values);
} else if (!(--remaining)) {
//成功,并且最后剩余的异步任务为0了
//说明所有任务都成功了,派发成功事件出去
//事件包含的上下文是当前任务前边的所有任务的一个集合
deferred.resolveWith(contexts, values);
}
};
},
progressValues, progressContexts, resolveContexts;
//如果只有一个任务,可以不用做维护状态的处理了
//只有大于1个任务才需要维护任务的状态
if (length > 1) {
progressValues = new Array(length);
progressContexts = new Array(length);
//事件包含的上下文是当前任务前边的所有任务的一个集合,逐步更新
resolveContexts = new Array(length);
for (; i < length; i++) {
if (resolveValues[i] && jQuery.isFunction(resolveValues[i].promise)) {
//如果是异步任务
resolveValues[i].promise()
//成功的时候不断更新自己的状态
.done(updateFunc(i, resolveContexts, resolveValues))
//当一个任务失败的时候,代表整个都失败了。直接派发一个失败即可
.fail(deferred.reject)
//正在处理的时候也要不断更新自己的状态
.progress(updateFunc(i, progressContexts, progressValues));
} else {
//如果是同步任务,则remain不应该计它在内
--remaining;
}
}
}
//传进来的任务都是同步任务
if (!remaining) {
deferred.resolveWith(resolveContexts, resolveValues);
}
return deferred.promise();
}
});
~~~
** 回顾技术,记录点滴,不正之处,请与指正 **