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()
差很少(但不一樣)。