jQuery.deferred()詳解
阿新 • • 發佈:2019-01-12
/** 請注意 這個程式碼是結合 阮老師 的部落格來實現的. * 網址 http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html * 如果有不懂的請看 阮老師 的網址 */ // JQ 1.5 版本之前的 AJAX 方法 /** * success : 這是一個成功的回撥函式 * error : 這是一個失敗的回撥函式 */ $.ajax({ type: "get", url: "./test1.json", data: "", dataType: "json", success: function(response) { console.log(response); }, error: function(err) { console.log(err); } }); // JQ 1.5 版本之後的 AJAX 方法 /** * done : 這是一個成功的回撥函式 * fail : 這是一個失敗的回撥函式 */ // (寫法1) $.ajax('./test1.json').done(function() { console.log('成功了 '); }).fail(function() { console.log('失敗了'); }) /** * then : then 接受兩個引數, 一個是成功的回撥函式, 一個是失敗的回撥函式 * 如果成功了,會呼叫第一個回撥函式, 如果失敗了, 會呼叫第二個回撥函式 */ // (寫法2) $.ajax('./test1.json').then(function() { console.log('成功了'); }, function() { console.log('失敗了'); }) /** * ------------------------------------------------ $.when() ------------------------------------------------------ * (引入 $.when() ) * 提供一種方法來執行零個或多個( Thenable \ then \ Deferred ) 物件的回撥函式,通常是表示非同步事件 * 如果沒有引數傳遞給 jQuery.when(),它會返回一個resolved狀態的Promise。 * 切記 : 如果傳引數 那麼 他將永遠返回的是 成功 的狀態, 失敗定義的回撥函式, 永遠不會被執行, 因為不會遭到拒絕 * 如果傳參是 不是一個 Deferred 或 Promise 物件, 那麼他將永遠返回的是 成功 的狀態, 失敗定義的回撥函式, 永遠不會被執行, 因為不會遭到拒絕 * * 上面說的兩種 都不是 非同步物件, 所以會直接返回 成功時的回撥 * 如果說是成功的回撥, 我們需要呼叫方法, done 和 fail 只是定義方法, 並沒有呼叫, 不呼叫 並不會執行!!! * resolve() : 這是呼叫成功的回撥, 也就是說 定義的 done 的回撥 * reject() : 這是呼叫失敗的回撥, 也就是說 定義的 fail 的回撥 * 如果定義了 兩個回撥, 並且也執行了 resolve 和 reject, 只會執行最先執行的方法. * 如果定義了 成功的 回撥, 卻執行了 reject 方法, 那麼並不會執行, 因為你沒有定義方法 */ $.when().done(function() { console.log('成功了'); /** 成功了 */ }).fail(function() { console.log('失敗了'); }) $.when({ testing: 123 }).done( function(x) { alert(x.testing); } /* alerts "123" */ ); var dtd = $.Deferred() $.when(dtd).done(function() { console.log('1111'); }).fail(function() { console.log('2222'); }) dtd.resolve() // 111 dtd.reject() // 不會執行,因為並沒有定義失敗的回撥 /** * $.when(引數1, 引數2, 引數3...) * 此方法接受多個引數, 但是返回的結果卻是不一樣的, 但是無非還是兩種狀態, 一種成功時的狀態, 一種失敗時的狀態 * 成功時的狀態 : 必須所有的物件都返回成功, 才會返回成功的回撥函式 * 失敗時的狀態 : 如果其中只要有一個返回失敗, 都會返回失敗的回撥函式 * 如果定義函式的時候給了引數, 呼叫函式的時候沒給引數, 那麼值將會是 undefined */ /** d1, d2 時定義了兩個 非同步 物件 */ var d1 = $.Deferred(); var d2 = $.Deferred(); /** 呼叫 when 方法, 並且設定 ( 此處只是設定或者說定義, 並不是呼叫 ) 成功的回撥*/ $.when().done(function(v1, v2) { console.log(v1); // "Fish" console.log(v2); // "Pizza" }); /** 呼叫成功時的方法 */ d1.resolve("Fish"); d2.resolve("Pizza"); /** 傳入多個引數的方法 */ var d1 = $.Deferred(); var d2 = $.Deferred(); var d3 = $.Deferred(); var d4 = $.Deferred(); var d5 = $.Deferred(); /** 呼叫 when 方法, 並且設定 ( 此處只是設定或者說定義, 並不是呼叫 ) 成功的回撥*/ $.when(d1, d2, d3, d4, d5).done(function(v1, v2, v3, v4, v5) { console.log(v1); // "Fish" console.log(v2); // "Pizza" console.log(v3); // undefined console.log(v4); // is Array [1, 2, 3] console.log(v5); // is Object {a: "a"} }); /** 呼叫成功時的方法 */ d1.resolve("Fish"); d2.resolve("Pizza"); d3.resolve(); d4.resolve(1, 2, 3); d5.resolve({ a: "a" }); /** ----------------------------------------------------------------------------------------------------------- */ /** * 定義一個很耗時的 wait 函式 * 這個地方不呼叫 resolve 就可以執行, 是因為 wait 並不是一個 promise 或者 deferred * done()方法會立即執行,起不到回撥函式的作用 * $.when()的引數只能是deferred物件,所以必須對wait()進行改寫 */ var wait = function() { var tasks = function() { console.log('執行完畢'); } setTimeout(tasks, 2000) } $.when(wait()) .done(function() { console.log("哈哈,成功了!"); }) .fail(function() { console.log("出錯啦!"); }); /** * 改寫 wait() * 再 wait 函式執行完之後, 函式內部呼叫了成功的方法就會自動執行 done 函式 * 當然你也可以 呼叫失敗的方法, 只需要把 `dtd.resolve()` 改成 `dtd.reject()` 即可 * Deferred 是有三個狀態的 * 第一個狀態 : 待開始的狀態 * 第二個狀態 : 成功時的狀態 * 第三個狀態 : 失敗時的狀態 */ // 第一種寫法(沒有封裝) var dtd = $.Deferred() // 建立一個 Deferred 物件 var wait = function(dtd) { // 要求傳入一個 deferred 物件, 不然這個函式沒有意義了 var tasks = function() { //這裡可以有大量的複雜操作 console.log("執行完畢"); // 執行完畢 dtd.resolve() // 表示非同步任務已經完成, 改變 deferred 物件的狀態 // dtd.reject() // 表示非同步任務失敗, 改變 deferred 物件的狀態 } setTimeout(tasks, 2000) // 等待 2m 後執行 return dtd // 一定要有返回值, 並且 一定是這個 物件 } $.when(wait(dtd)) .done(function() { console.log("哈哈,成功了!"); }) // 哈哈,成功了! .fail(function() { console.log("出錯啦!"); }); // // 第二種寫法 (封裝版) function waitHandle() { var dtd = $.Deferred() var wait = function(dtd) { var tasks = function() { console.log('執行完畢') // 執行完畢 dtd.resolve() // dtd.reject() } setTimeout(tasks, 2000) return dtd } return wait(dtd) } var w = waitHandle() // 此處同樣可是使用 then 方法來呼叫 w.then(function() { console.log('ok 1'); // ok 1 }, function() { console.log('err 1'); }).then(function() { console.log('ok 1'); // ok 1 }, function() { console.log('err 1'); }) /** * 這樣呢 : 有一個弊端就是, 可以手動修改狀態. * 舉梨 : 比如 我們函式中, 定義的是 dtd.resolve() 方法,但是我們再程式的最後一行加上 `w.reject()` * 結果 就很有意思了, 會再程式以執行的時候, 改變 dtd 的狀態, 然後立即執行 `done()`, 等到 延時器 結束的時候再執行 `console.log('執行完畢')` * 這樣就不是我們想要的了 * 所以 : 就引出了 deferred.promise() 方法. */ // + w.resolve() /** -------------------------------------- deferred.promise() -------------------------------------------------- */ /** 只需要再 wait() 函式裡面 * return 的不在是`dtd` 而是 return `dtd.promise` * 這樣我們再外邊就沒有辦法修改了 * 給大家 將一個 思路 * 就是說 函式裡面的 dtd.resolve 和 dtd.reject 這兩個函式是主動觸發的 * 還有就是 w.then() 或者 done 還有 fail 這幾個函式是被動監聽的 */ // 第一種方法 function waitHandle() { var dtd = $.Deferred() var wait = function(dtd) { var tasks = function() { console.log('執行完畢') // 執行完畢 dtd.resolve() // dtd.reject() } setTimeout(tasks, 2000) // return dtd return dtd.promise() // 返回的不再是 dtd 而是 dtd 的 promise 物件. } return wait(dtd) } var w = waitHandle() // 經過上面的修改, 現在 w 接受的就是一個 promise 物件 w.then(function() { console.log('ok 1'); // ok 1 }, function() { console.log('err 1'); }).then(function() { console.log('ok 1'); // ok 1 }, function() { console.log('err 1'); }) w.reject() // 而執行這句話的時候會直接報錯 // 第二種方法. 可以直接在wait物件上部署deferred介面。 var dtd = $.Deferred(); // 生成Deferred物件 var wait = function(dtd) { var tasks = function() { console.log("執行完畢!"); dtd.resolve(); // 改變Deferred物件的執行狀態 }; setTimeout(tasks, 5000); }; dtd.promise(wait); wait.done(function() { console.log("哈哈,成功了!"); }) .fail(function() { console.log("出錯啦!"); }); wait(dtd); /** * 總結 : deferred 物件的方法 * 1. $.Deferred() 生成一個新的物件 * 2. deferred.done() 指定操作成功時的回撥 * 3. deferred.fail() 指定操作失敗時的回撥 * 4. deferred.promise() 沒有引數的時候, 返回一個新的 deferred 物件, 該物件的執行狀態無法被改變; 接受引數時, 作用為引數物件物件上部署 deferred 介面 * 5. deferred.resolve() 手動改變 deferred 物件的執行狀態為 "完成" ,從而立即觸發 done 或者 then 的第一個回撥函式 * 6. deferred.reject() 手動改變 deferred 物件的執行狀態為 "失敗" ,從而立即觸發 fail 或者 then 的第二個回撥函式 * 7. $.when() 為單個或多個操作指定回撥函式 * 8. deferred.then() 此方法就是把 done 和 fail 方法的合併, 裡面有兩個引數 第一個時成功時的回撥, 第二個時失敗時的回撥,可以只寫一個. * 9. deferred.always() 這個方法之前沒說過,他的作用是, 不管呼叫的是 deferred.resolve 還是 deferred.reject, 總是最後才執行 */ $.ajax("test.html") .always(function() { alert("已執行!"); });