deferred對象是jQuery對Promises接口的實現。它是非同步操作的通用接口,可以被看作是一個等待完成的任務,開發者通過一些通過的接口對其進行設置。事實上,它扮演代理人(proxy)的角色,將那些非同步操作包裝成具有某些統一特性的對象,典型例子就是Ajax操作、網頁動畫、web worker等等。
			jQuery的所有Ajax操作函數,默認返回的就是一個deferred對象。
			Promises是什么
			由于JavaScript單線程的特點,如果某個操作耗時很長,其他操作就必需排隊等待。為了避免整個程序失去響應,通常的解決方法是將那些排在后面的操作,寫成“回調函數”(callback)的形式。這樣做雖然可以解決問題,但是有一些顯著缺點:
			1.回調函數往往寫成函數參數的形式,導致函數的輸入和輸出非常混亂,整個程序的可閱讀性差;
		2.回調函數往往只能指定一個,如果有多個操作,就需要改寫回調函數。
		3.整個程序的運行流程被打亂,除錯和調試的難度都相應增加。
			Promises就是為了解決這些問題而提出的,它的主要目的就是取代回調函數,成為非同步操作的解決方案。它的核心思想就是讓非同步操作返回一個對象,其他操作都針對這個對象來完成。比如,假定ajax操作返回一個Promise對象。
			復制代碼代碼如下:
			
		var promise = get('http://m.survivalescaperooms.com');
		然后,Promise對象有一個then方法,可以用來指定回調函數。一旦非同步操作完成,就調用指定的回調函數。	
		復制代碼代碼如下:
			
		promise.then(function (content) {
		  console.log(content)
		})
		可以將上面兩段代碼合并起來,這樣程序的流程看得更清楚。	
		復制代碼代碼如下:
			
		get('http://m.survivalescaperooms.com').then(function (content) {
		  console.log(content)
		})
			 
			在1.7版之前,jQuery的Ajax操作采用回調函數。
			復制代碼代碼如下:
			
		$.ajax({
		    url:"/echo/json/",
		    success: function(response)
		    {
		       console.info(response.name);
		    }
		});
			 
			1.7版之后,Ajax操作直接返回Promise對象,這意味著可以用then方法指定回調函數。
			復制代碼代碼如下:
			
		$.ajax({
		    url: "/echo/json/",
		}).then(function (response) {
		    console.info(response.name);
		});
			 
			deferred對象的方法
			$.deferred()方法
			作用是生成一個deferred對象。
			復制代碼代碼如下:
			
		var deferred = $.deferred();
			 
			done() 和 fail()
			這兩個方法都用來綁定回調函數。done()指定非同步操作成功后的回調函數,fail()指定失敗后的回調函數。
			復制代碼代碼如下:
			
		var deferred = $.Deferred();
		deferred.done(function(value) {
		   alert(value);
		});
			 
			它們返回的是原有的deferred對象,因此可以采用鏈式寫法,在后面再鏈接別的方法(包括done和fail在內)。
			resolve() 和 reject()
			這兩個方法用來改變deferred對象的狀態。resolve()將狀態改為非同步操作成功,reject()改為操作失敗。
			復制代碼代碼如下:
			
		var deferred = $.Deferred();
		deferred.done(function(value) {
		   alert(value);
		});
		deferred.resolve("hello world");
		一旦調用resolve(),就會依次執行done()和then()方法指定的回調函數;一旦調用reject(),就會依次執行fail()和then()方法指定的回調函數。	
		 
			state方法
			該方法用來返回deferred對象目前的狀態。
			復制代碼代碼如下:
			
		var deferred = new $.Deferred();
		deferred.state();  // "pending"
		deferred.resolve();
		deferred.state();  // "resolved"
		該方法的返回值有三個:	
		 
			1.pending:表示操作還沒有完成。
		2.resolved:表示操作成功。
		3.rejected:表示操作失敗。
			notify() 和 progress()
			progress()用來指定一個回調函數,當調用notify()方法時,該回調函數將執行。它的用意是提供一個接口,使得在非同步操作執行過程中,可以執行某些操作,比如定期返回進度條的進度。
			復制代碼代碼如下:
			
		 var userProgress = $.Deferred();
		    var $profileFields = $("input");
		    var totalFields = $profileFields.length
		    userProgress.progress(function (filledFields) {
		        var pctComplete = (filledFields/totalFields)*100;
		        $("#progress").html(pctComplete.toFixed(0));
		    }); 
		    userProgress.done(function () {
		        $("#thanks").html("Thanks for completing your profile!").show();
		    });
		    $("input").on("change", function () {
		        var filledFields = $profileFields.filter("[value!='']").length;
		        userProgress.notify(filledFields);
		        if (filledFields == totalFields) {
		            userProgress.resolve();
		        }
		    });
			 
			then()
			then()的作用也是指定回調函數,它可以接受三個參數,也就是三個回調函數。第一個參數是resolve時調用的回調函數,第二個參數是reject時調用的回調函數,第三個參數是progress()方法調用的回調函數。
			復制代碼代碼如下:
			
		deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
		在jQuery 1.8之前,then()只是.done().fail()寫法的語法糖,兩種寫法是等價的。在jQuery 1.8之后,then()返回一個新的deferred對象,而done()返回的是原有的deferred對象。如果then()指定的回調函數有返回值,該返回值會作為參數,傳入后面的回調函數。	
		 
			 
			復制代碼代碼如下:
			
		var defer = jQuery.Deferred();
		defer.done(function(a,b){
		            return a * b;
		}).done(function( result ) {
		            console.log("result = " + result);
		}).then(function( a, b ) {
		            return a * b;
		}).done(function( result ) {
		            console.log("result = " + result);
		}).then(function( a, b ) {
		            return a * b;
		}).done(function( result ) {
		            console.log("result = " + result);
		});
		defer.resolve( 2, 3 );
			 
			在jQuery 1.8版本之前,上面代碼的結果是:
			復制代碼代碼如下:
			
		result = 2 
		result = 2 
		result = 2 
		在jQuery 1.8版本之后,返回結果是	
		復制代碼代碼如下:
			
		result = 2 
		result = 6 
		result = NaN 
		這一點需要特別引起注意。	
		復制代碼代碼如下:
			
		$.ajax( url1, { dataType: "json" } )
		.then(function( data ) {
		    return $.ajax( url2, { data: { user: data.userId } } );
		}).done(function( data ) {
		  // 從url2獲取的數據
		});
			 
			上面代碼最后那個done方法,處理的是從url2獲取的數據,而不是從url1獲取的數據。
			利用then()會修改返回值這個特性,我們可以在調用其他回調函數之前,對前一步操作返回的值進行處理。
			復制代碼代碼如下:
			
		var post = $.post("/echo/json/")
		    .then(function(p){
		        return p.firstName;
		    });
		post.done(function(r){ console.log(r); });
		上面代碼先使用then()方法,從返回的數據中取出所需要的字段(firstName),所以后面的操作就可以只處理這個字段了。	
		 
			有時,Ajax操作返回json字符串里面有一個error屬性,表示發生錯誤。這個時候,傳統的方法只能是通過done()來判斷是否發生錯誤。通過then()方法,可以讓deferred對象調用fail()方法。
			復制代碼代碼如下:
			
		var myDeferred = $.post('/echo/json/', {json:JSON.stringify({'error':true})})
		    .then(function (response) {
		            if (response.error) {
		                return $.Deferred().reject(response);
		            }
		            return response;
		        },function () {
		            return $.Deferred().reject({error:true});
		        }
		    );
		myDeferred.done(function (response) {
		        $("#status").html("Success!");
		    }).fail(function (response) {
		        $("#status").html("An error occurred");
		    });
			 
			always()
			always()也是指定回調函數,不管是resolve或reject都要調用。
			pipe方法
			pipe方法接受一個函數作為參數,表示在調用then方法、done方法、fail方法、always方法指定的回調函數之前,先運行pipe方法指定的回調函數。它通常用來對服務器返回的數據做初步處理。
			promise對象
			大多數情況下,我們不想讓用戶從外部更改deferred對象的狀態。這時,你可以在deferred對象的基礎上,返回一個針對它的promise對象。我們可以把后者理解成,promise是deferred的只讀版,或者更通俗地理解成promise是一個對將要完成的任務的承諾。
			你可以通過promise對象,為原始的deferred對象添加回調函數,查詢它的狀態,但是無法改變它的狀態,也就是說promise對象不允許你調用resolve和reject方法。
			 
			復制代碼代碼如下:
			
		function getPromise(){
		    return $.Deferred().promise();
		}
		try{
		    getPromise().resolve("a");
		} catch(err) {
		    console.log(err);
		}
			 
			上面的代碼會出錯,顯示TypeError {} 。
			jQuery的ajax() 方法返回的就是一個promise對象。此外,Animation類操作也可以使用promise對象。
			復制代碼代碼如下:
			
		var promise = $('div.alert').fadeIn().promise();
			 
			$.when()方法
			$.when()接受多個deferred對象作為參數,當它們全部運行成功后,才調用resolved狀態的回調函數,但只要其中有一個失敗,就調用rejected狀態的回調函數。它相當于將多個非同步操作,合并成一個。
			復制代碼代碼如下:
			
		$.when(
		    $.ajax( "/main.php" ),
		    $.ajax( "/modules.php" ),
		    $.ajax( "/lists.php" )
		).then(successFunc, failureFunc);
			 
			上面代碼表示,要等到三個ajax操作都結束以后,才執行then方法指定的回調函數。
			when方法里面要執行多少個操作,回調函數就有多少個參數,對應前面每一個操作的返回結果。
			復制代碼代碼如下:
			
		$.when(
		    $.ajax( "/main.php" ),
		    $.ajax( "/modules.php" ),
		    $.ajax( "/lists.php" )
		).then(function (resp1, resp2, resp3){
		    console.log(resp1);
		    console.log(resp2);
		    console.log(resp3);
		});
			 
			上面代碼的回調函數有三個參數,resp1、resp2和resp3,依次對應前面三個ajax操作的返回結果。
			when方法的另一個作用是,如果它的參數返回的不是一個Deferred或Promise對象,那么when方法的回調函數將 立即運行。
			復制代碼代碼如下:
			
		$.when({testing: 123}).done(function (x){
		  console.log(x.testing); // "123"
		});
			 
			上面代碼中指定的回調函數,將在when方法后面立即運行。
			利用這個特點,我們可以寫一個具有緩存效果的異步操作函數。也就是說,第一次調用這個函數的時候,將執行異步操作,后面再調用這個函數,將會返回緩存的結果。
			復制代碼代碼如下:
			
		function maybeAsync( num ) {
		  var dfd = $.Deferred();
		  if ( num === 1 ) {
		    setTimeout(function() {
		      dfd.resolve( num );
		    }, 100);
		    return dfd.promise();
		  }
		  return num;
		}
		$.when(maybeAsync(1)).then(function (resp){
		  $('#target').append('<p>' + resp + '</p>');
		});
		$.when(maybeAsync(0)).then(function (resp){
		  $('#target').append( '<p>' + resp + '</p>');
		});
			 
			上面代碼表示,如果maybeAsync函數的參數為1,則執行異步操作,否則立即返回緩存的結果。
			實例
			wait方法
			我們可以用deferred對象寫一個wait方法,表示等待多少毫秒后再執行。
			復制代碼代碼如下:
			
		$.wait = function(time) {
		  return $.Deferred(function(dfd) {
		    setTimeout(dfd.resolve, time);
		  });
		}
		使用方法如下:	
		復制代碼代碼如下:
			
		$.wait(5000).then(function() {
		  alert("Hello from the future!");
		});
			 
			改寫setTimeout方法
			在上面的wait方法的基礎上,還可以改寫setTimeout方法,讓其返回一個deferred對象。
			復制代碼代碼如下:
			
		function doSomethingLater(fn, time) {
		  var dfd = $.Deferred();
		  setTimeout(function() {
		    dfd.resolve(fn());
		  }, time || 0);
		  return dfd.promise();
		}
		var promise = doSomethingLater(function (){
		  console.log( '已經延遲執行' );
		}, 100);
			 
			自定義操作使用deferred接口
			我們可以利用deferred接口,使得任意操作都可以用done()和fail()指定回調函數。
			復制代碼代碼如下:
			
		Twitter = {
		  search:function(query) {
		    var dfr = $.Deferred();
		    $.ajax({
		     url:"http://search.twitter.com/search.json",
		     data:{q:query},
		     dataType:'jsonp',
		     success:dfr.resolve
		    });
		    return dfr.promise();
		  }
		}
			 
			使用方法如下:
			復制代碼代碼如下:
			
		Twitter.search('intridea').then(function(data) {
		  alert(data.results[0].text);
		});
		deferred對象的另一個優勢是可以附加多個回調函數。	
		復制代碼代碼如下:
			
		function doSomething(arg) {
		  var dfr = $.Deferred();
		  setTimeout(function() {
		    dfr.reject("Sorry, something went wrong.");
		  });
		  return dfr;
		}
		doSomething("uh oh").done(function() {
		  alert("Won't happen, we're erroring here!");
		}).fail(function(message) {
		  alert(message)
		});