deferred.promise() 和 .promise()
这两个API语法几乎一样,但是有着很大的差别。deferred.promise()是Deferred实例的一个方法,他返回一个Deferred.Promise实例。一个Deferred.Promise对象可以理解为是deferred对象的一个视图,它只包含deferred对象的一组方法,包括:done(),then(),fail(),isResolved(), isRejected(), always(),这些方法只能观察一个deferred的状态,而无法更改deferred对象的内在状态。这非常适合于API的封装。例如一个deferred对象的持有者可以根据自己的需要控制deferred状态的状态(resolved或者rejected),但是可以把这个deferred对象的Promise对象返回给其它的观察者,观察者只能观察状态的变化绑定相应的回调函数,但是无法更改deferred对象的内在状态,从而起到很好的隔离保护作用。
deferred.promise()
$(function(){
//
var deferred = $.Deferred();
var promise = deferred.promise();
var doSomething = function(promise) {
promise.done(function(){
alert('deferred resolved.');
});
};
deferred.resolve();
doSomething(promise);
})
deferred.promise()也可以接受一个object参数,此时传入的object将被赋予Promise的方法,并作为结果返回。
// Existing object var obj = { hello: function( name ) { alert( "Hello " + name ); } }, // Create a Deferred defer = $.Deferred(); // Set object as a promise defer.promise( obj ); // Resolve the deferred defer.resolve( "John" ); // Use the object as a Promise obj.done(function( name ) { this.hello( name ); // will alert "Hello John" }).hello( "Karl" ); // will alert "Hello Karl"
deferred.promise() 只是阻止其他代码来改变这个 deferred 对象的状态。可以理解成,通过 deferred.promise() 方法返回的 deferred promise 对象,是没有 resolve ,reject, progress , resolveWith, rejectWith , progressWith 这些可以改变状态的方法,你只能使用 done, then ,fail 等方法添加 handler 或者判断状态。
deferred.promise() 改变不了 deferred 对象的状态,作用也不是保证目前的状态不变,它只是保证你不能通过 deferred.promise() 返回的 deferred promise 对象改变 deferred 对象的状态。如果我们这个地方直接返回 dtd,也是可以工作的,.done 的处理函数还是会等到 dtd.resolve() 之后才会执行.
具体在那篇博客的例子, 如果我们把代码改成如下的形式:
var dtd = $.Deferred(); // 新建一个deferred对象 var wait = function(dtd){ var tasks = function(){ alert("执行完毕!"); dtd.resolve(); // 改变deferred对象的执行状态 }; setTimeout(tasks,5000); return dtd; }; $.when(wait(dtd)) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); });
这样的执行结果和先前返回 dtd.promise 的结果是一样的。
差别在什么地方呢?如果我们把 $.when 的这块的代码改成这样的:
var d = wait(dtd); $.when(d) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); }); d.resolve();
我们会发现 alert(“哈哈,成功了!”) 会立即执行,“执行完毕”却需要5秒后才弹出来。
但是如果我们 wait 函数最后是 return dtd.promise() 这里 d.resolve() 就会报错了,因为对象 d 不存在 resolve() 方法。
同样如果我们把代码改成:
var dtd = $.Deferred(); // 新建一个deferred对象 var wait = function(dtd){ var tasks = function(){ alert("执行完毕!"); dtd.resolve(); // 改变deferred对象的执行状态 }; setTimeout(tasks,5000); return dtd.promise(); }; dtd.resolve(); $.when( wait(dtd)) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); });
我们也可以发现 alert(“哈哈,成功了!”) 会立即执行,因为 dtd 这个 deferred 对象在被传入 wait 之前,已经被 resolve() 了,而 deferred 对象一旦被 resolve 或者 reject 之后,状态是不会改变的。
然后我们再把 $.wait 这块的代码改成:
$.when( wait(dtd)) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); }); dtd.resolve();
我们也会发现 alert(“哈哈,成功了!”); 被立即执行,虽然 wait(dtd) 执行的时候, dtd 还没有被 resolve,而且 wait 方法返回的是 dtd.promise(), 但是 dtd 这个原始的 deferred 对象是暴露在外面的,我们还是可以从外面改变它的状态。
于是,如果我们真的不想让其他代码能改变 wait 方法内部的 deferred 对象的状态,那我们应该写成这样:
var wait = function(){ var dtd = $.Deferred(); // 新建一个deferred对象 var tasks = function(){ alert("执行完毕!"); dtd.resolve(); // 改变deferred对象的执行状态 }; setTimeout(tasks,5000); return dtd.promise(); }; $.when( wait()) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); });
也就是不要把 deferred 直接暴露出来,最后返回 deferred.promise() ,让其他地方的代码只能添加 handler 。
.promise()