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); })
// 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()
首先這不是Deferred實例的方法! 該方法是jQuery實例的方法。此方法用於一組類型的動作(例如動畫)全部完成後返回一個Promise對象,供事件監聽器監聽其狀態並執行相應的處理函數。
此方法接受兩個可選參數:.promise( [type,] [target] )
type:隊列的類型,預設值是fx,fx即jQuery物件的動畫.
targetObject :要賦予Promise行為的對象,
這兩個參數是可選的。其中第一個參數(我)目前除了fx還沒有找到其他的值類型。因此一般都是用於動畫的監控,在動畫完成後做一些操作。
範例:沒有動畫效果直接回傳一個resolved狀態的promise物件
var div = $( "<div />" ); div.promise().done(function( arg1 ) { // 将会被马上触发 alert( this === div && arg1 === div ); });
範例:在動畫效果全部完成後觸發done()監聽函式
<!DOCTYPE html> <html> <head> <style> div { height: 50px; width: 50px; float: left; margin-right: 10px; display: none; background-color: #090; } </style> <script src="http://code.jquery.com/jquery-latest.js"></script> </head> <body> <button>Go</button> <p>Ready...</p> <div></div> <div></div> <div></div> <div></div> <script> $("button").bind( "click", function() { $("p").append( "Started..."); //每个div执行动画效果 $("div").each(function( i ) { $( this ).fadeIn().fadeOut( 1000 * (i+1) ); }); //$("div")包含一组div,在所有的div都完成自己的动画效果后触发done()函数 $( "div" ).promise().done(function() { $( "p" ).append( " Finished! " ); }); }); </script> </body> </html>