ES6 和 jQuery 都有 Deffered 和 Promise,可是略有不一樣。不過它們的做用能夠簡單的用兩句話來描述javascript
在 jQuery 中html
var deferred = $.Deferred(); var promise = deferred.promise();
在 ES6 中java
var deferred = Promise.defer(); var promise= defered.promise;
MDN 宣佈 Deferred 在 Gecko 30 中被申明爲過時,不該該再使用,而應該用
new Promise()
來代替。關於new Promise()
將在後面說明。jquery
jQuery 中最經常使用的 Promise 對象是 $.ajax()
返回的,最經常使用的方法不是 then
,而是 done
、fail
和 always
。除了 $.ajax()
外,jQuery 也提供了 $.get()
、$.post()
和 $.getJSON()
等簡化 Ajax 調用,它們返回的和 $.ajax()
的返回值同樣,是個 Promise 對象。ajax
實際上
$.ajax()
返回的是一個 jqXHR 對象。但 jqXHR 實現了 jQuery 的 Promise 接口,因此也是一個 Promise 對象。json
done()
、fail()
和 always()
done()
添加 deferred.resolve()
的回調,fail()
添加 deferred.reject()
的回調。因此在 Ajax 調用成功的狀況下執行 done()
添加的回調,調用失敗時執行 fail()
添加的回調。但無論成功與否,都會執行 always()
添加的回調。api
這裏 done()
、fail()
和 always()
都是以相似事件的方式添加回調,也就意味着,無論執行屢次次 done()
、fail()
或 always()
,它們添加的若干回調都會在符合的條件下依次執行。promise
通常狀況下會這樣執行 Ajax服務器
// 禁用按鈕以免重複提交 $("#theButton").prop({ disabled: true }); // 調用 Ajax 提交數據,假設返回的是 JSON 數據 var jqxhr = $.ajax("do/example", { type: "post", dataType: "json", data: getFormData() }); jqxhr.done(function(jsonObject) { // Ajax 調用成功 console.log("success with data", jsonObject); }).fail(function() { // Ajax 調用失敗 console.log("failed") }).always(function() { // 無論成功與否,都會執行,取消按鈕的禁用狀態 $("#theButton").prop({ disabled: false }); });
上面是最普通最經常使用的用法,可是在一個項目中老是這麼寫 Ajax,有點累,稍微約定一下再封裝一下就使用起來就會便捷得多。首先,假設咱們定義返回的 JSON 是這樣的格式:markdown
{ "code": "int, 0 表示成功,其它值表示出錯", "message": "string, 附加的消息,可選", "data": "object,附加的數據,可選 }
而後爲項目公共類 app
定義一個 ajax
方法
app.ajax = function(button, url, data) { if (button) { button.prop("disabled", true); } return $.ajax(url, { type: "post", dataType: "json", data: data }).done(function(json) [ if (json.code !== 0) { showError(json.message || "操做發生錯誤"); } }).fail(function() { showError("服務器錯誤,請稍後再試"); }).always(function() { if (button) { button.prop("disabled", false); } }); }; // 調用 app.ajax("do/example", getFormData()).done(function(json) { if (json.code === 0) { // 只須要處理正確的狀況啦 } });
不過仍是有點不爽,若是不須要判斷 json.code === 0
就更好了。這個……能夠本身用一個 Deferred 來處理:
app.ajax = function(button, url, data) { if (button) { button.prop("disabled", true); } var deferred = $.Deferred(); $.ajax(url, { type: "post", dataType: "json", data: data }).done(function(json) [ if (json.code !== 0) { showError(json.message || "操做發生錯誤"); deferred.reject(); } else { deferred.resolve(json); } }).fail(function() { showError("服務器錯誤,請稍後再試"); deferred.reject(); }).always(function() { if (button) { button.prop("disabled", false); } }); return deferred.promise(); }; // 調用 app.ajax("do/example", getFormData()).done(function(json) { // json.code === 0 老是成立 // 正常處理 json.data 就好 });
注意,這裏已經不是直接返回 $.ajax()
的結果 jqXHR 對象了,返回的是新建 Deferred
對象的 promise
對象。
複習了 Ajax,如今須要切入正題,找到 jQuery Promise 和 ES6 Promise 接近的地方——then()
。
deferred.then()
在 jQuery 1.8 之前(不含 1.8,好比 jQuery 1.7.2),deferred.then()
就是一個把 done()
和 fail()
放在一塊兒的語法糖。jQuery 在 1.8 版本的時候修改了 deferred.then()
的行爲,使 then()
的行爲與 Promise 的 then()
類似。從 jQuery 的文檔能夠看到 1.8 版本的變化——幹掉了 callback,換成了 filter:
// version added: 1.5, removed: 1.8 deferred.then( doneCallbacks, failCallbacks ) // version added: 1.7, removed: 1.8 deferred.then( doneCallbacks, failCallbacks [, progressCallbacks ] ) // version added: 1.8 deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
能夠簡單的把 callback 看成一個事件處理,值用於 callback 以後通常不會改變;而 filter 不一樣,一個值傳入 filter 再從 filter 返回出來,可能已經變了。仍是舉個例子來講明
var deferred = $.Deferred(); var promise = deferred.promise(); promise.then(function(v) { console.log(`then with ${v}`); }).done(function(v) { console.log(`done with ${v}`); }); deferred.resolve("resolveData");
在 jQuery 1.7.2 中的結果
then with resolveData done with resolveData
在 jQuery 1.8.0 中的結果
then with resolveData done with undefined
從上面來看,jQuery 的 deferred.then()
語義和 ES6 Promise.then()
語義基本一致。若是把上面的 app.ajax
換成 then()
實現會有助於對 ES6 Promise 的理解。
app.ajax = function(button, url, data) { if (button) { button.prop("disabled", true); } return $.ajax(url, { type: "post", dataType: "json", data: data }).then(function(json) { if (json.code !== 0) { showError(json.message || "操做發生錯誤"); return $.Deferred().reject().promise(); } else { return $.Deferred().resolve(json).promise(); } }, function() { showError("服務器錯誤,請稍後再試"); deferred.reject(); }).always(function() { if (button) { button.prop("disabled", false); } }); }; // 調用方式沒變,用 done,也能夠用 then app.ajax("do/example", getFormData()).done(function(json) { // json.code === 0 老是成立 // 正常處理 json.data 就好 });
上面的代碼太長,提煉一下關鍵部分(示意,不能運行)
var promise = $.ajax(); promise.then(function(data) { // resolve return data.code ? new Promise().reject() : new Promise().resolve(data); // 若是沒有錯,就返回一個新的 promise,並使用 data 來 resolve, // 也能夠直接返回 data, // 這樣後面 then 的 resolve 部分才能收到數據 }, function() { // rejected }); // 調用階段 promise.then(function(data) { // 處理 data });
也許你沒注意到,其實上面的代碼基本上就是 ES6 的 Promise 了。下面正式用 ES6 Promise 改寫上面的示意代碼
var promise = new Promise(function(resolve, reject) { $.ajax().then(resolve, reject); // 上面這句沒看懂?那換成這樣你必定會懂 // $.ajax().then(function(data) { // resolve(data); // }, function() { // reject(); // }); }).then(function(data) { return data.code ? Promise.reject() : Promise.resolve(data); // 這裏 Promise.resolve(data) 一樣能夠直接替換爲 data }); // 調用沒變 promise.then(function(data) { // 處理 data });
怎麼樣,差異不大吧。不知不覺就會 ES6 Promise 了!
上面已經把 ES6 的 Promise 帶出來了,如今只須要把經常使用方法列出來做爲參考便可
注意,小寫的
promise
表示Promise
對象
new Promise(executor)
,產生一個新的 Promise 對象
executor(resolve, reject)
executor
、resolve
和reject
均爲函數,在executor
中,正確處理調用resolve()
返回數據,異常處理直接throw new Error(...)
或調reject()
返回數據。
Promise.resolve(data)
,產生 Promise 對象並 resolve
Promise.reject()
,產生 Promise 對象並 reject
promise.then(onResolve, onReject)
,而後……繼續處理
promise.catch(onReject)
,project.then(null, onReject)
的語法糖,和 jQuery 的 promise.fail()
差很少(但不一樣)。