這一節我們來簡單介紹下Deferred和Promise之間的關系
## 4.4.1\. 什么是Deferred?
說起Promise,我想大家一定同時也聽說過Deferred這個術語。比如?[jQuery.Deferred](http://api.jquery.com/category/deferred-object/)?和?[JSDeferred](http://cho45.stfuawsc.com/jsdeferred/)?等,一定都是大家非常熟悉的內容了。
Deferred和Promise不同,它沒有共通的規范,每個Library都是根據自己的喜好來實現的。
在這里,我們打算以?[jQuery.Deferred](http://api.jquery.com/category/deferred-object/)?類似的實現為中心進行介紹。
## 4.4.2\. Deferred和Promise的關系
簡單來說,Deferred和Promise具有如下的關系。
* Deferred 擁有 Promise
* Deferred 具備對 Promise的狀態進行操作的特權方法(圖中的"特権メソッド")

Figure 13\. Deferred和Promise
我想各位看到此圖應該就很容易理解了,Deferred和Promise并不是處于競爭的關系,而是Deferred內涵了Promise。
> 這是jQuery.Deferred結構的簡化版。當然也有的Deferred實現并沒有內涵Promise。
光看圖的話也許還難以理解,下面我們就看看看怎么通過Promise來實現Deferred。
## 4.4.3\. Deferred top on Promise
基于Promise實現Deferred的例子。
deferred.js
~~~
function Deferred() {
this.promise = new Promise(function (resolve, reject) {
this._resolve = resolve;
this._reject = reject;
}.bind(this));
}
Deferred.prototype.resolve = function (value) {
this._resolve.call(this.promise, value);
};
Deferred.prototype.reject = function (reason) {
this._reject.call(this.promise, reason);
};
~~~
我們再將之前使用Promise實現的?[`getURL`](http://liubin.github.io/promises-book/#xhr-promise.js)?用Deferred改寫一下。
xhr-deferred.js
~~~
function Deferred() {
this.promise = new Promise(function (resolve, reject) {
this._resolve = resolve;
this._reject = reject;
}.bind(this));
}
Deferred.prototype.resolve = function (value) {
this._resolve.call(this.promise, value);
};
Deferred.prototype.reject = function (reason) {
this._reject.call(this.promise, reason);
};
function getURL(URL) {
var deferred = new Deferred();
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
deferred.resolve(req.responseText);
} else {
deferred.reject(new Error(req.statusText));
}
};
req.onerror = function () {
deferred.reject(new Error(req.statusText));
};
req.send();
return deferred.promise;
}
// 運行示例
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){
console.log(value);
}).catch(console.error.bind(console));
~~~
所謂的能對Promise狀態進行操作的特權方法,指的就是能對promise對象的狀態進行resolve、reject等調用的方法,而通常的Promise的話只能在通過構造函數傳遞的方法之內對promise對象的狀態進行操作。
我們來看看Deferred和Promise相比在實現上有什么異同。
xhr-promise.js
~~~
function getURL(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
// 運行示例
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){
console.log(value);
}).catch(console.error.bind(console));
~~~
對比上述兩個版本的?`getURL`?,我們發現它們有如下不同。
* Deferred 的話不需要將代碼用Promise括起來
* 由于沒有被嵌套在函數中,可以減少一層縮進
* 反過來沒有Promise里的錯誤處理邏輯
在以下方面,它們則完成了同樣的工作。
* 整體處理流程
* 調用?`resolve`、`reject`?的時機
* 函數都返回了promise對象
由于Deferred包含了Promise,所以大體的流程還是差不多的,不過Deferred有用對Promise進行操作的特權方法,以及高度自由的對流程控制進行自由定制。
比如在Promise一般都會在構造函數中編寫主要處理邏輯,對?`resolve`、`reject`?方法的調用時機也基本是很確定的。
~~~
new Promise(function (resolve, reject){
// 在這里進行promise對象的狀態確定
});
~~~
而使用Deferred的話,并不需要將處理邏輯寫成一大塊代碼,只需要先創建deferred對象,可以在任何時機對?`resolve`、`reject`?方法進行調用。
~~~
var deferred = new Deferred();
// 可以在隨意的時機對 `resolve`、`reject` 方法進行調用
~~~
上面我們只是簡單的實現了一個?**Deferred**?,我想你已經看到了它和?**Promise**?之間的差異了吧。
如果說Promise是用來對值進行抽象的話,Deferred則是對處理還沒有結束的狀態或操作進行抽象化的對象,我們也可以從這一層的區別來理解一下這兩者之間的差異。
換句話說,Promise代表了一個對象,這個對象的狀態現在還不確定,但是未來一個時間點它的狀態要么變為正常值(FulFilled),要么變為異常值(Rejected);而Deferred對象表示了一個處理還沒有結束的這種事實,在它的處理結束的時候,可以通過Promise來取得處理結果。
如果各位讀者還想深入了解一下Deferred的話,可以參考下面的這些資料。
* [Promise & Deferred objects in JavaScript Pt.1: Theory and Semantics.](http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt1-theory-and-semantics)
* [Twisted 入門 — Twisted Intro](http://skitazaki.appspot.com/translation/twisted-intro-ja/index.html)
* [Promise anti patterns · petkaantonov/bluebird Wiki](https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns#the-deferred-anti-pattern)
* [Coming from jQuery · kriskowal/q Wiki](https://github.com/kriskowal/q/wiki/Coming-from-jQuery)
> Deferred最初是在Python的?[Twisted](https://twistedmatrix.com/trac/)?框架中被提出來的概念。 在JavaScript領域可以認為它是由?[MochiKit.Async](http://mochi.github.io/mochikit/doc/html/MochiKit/Async.html)?、?[dojo/Deferred](http://dojotoolkit.org/reference-guide/1.9/dojo/Deferred.html)?等Library引入的。
- 前言
- 第一章 - 什么是Promise
- 1.1. 什么是Promise
- 1.2. Promise簡介
- 1.3. 編寫Promise代碼
- 第二章 - 實戰Promise
- 2.1. Promise.resolve
- 2.2. Promise.reject
- 2.3. 專欄: Promise只能進行異步操作?
- 2.4. Promise#then
- 2.5. Promise#catch
- 2.6. 專欄: 每次調用then都會返回一個新創建的promise對象
- 2.7. Promise和數組
- 2.8. Promise.all
- 2.9. Promise.race
- 2.10. then or catch?
- 第三章 - Promise測試
- 3.1. 基本測試
- 3.2. Mocha對Promise的支持
- 3.3. 編寫可控測試(controllable tests)
- 第四章 - Advanced
- 4.1. Promise的實現類庫(Library)
- 4.2. Promise.resolve和Thenable
- 4.3. 使用reject而不是throw
- 4.4. Deferred和Promise
- 4.5. 使用Promise.race和delay取消XHR請求
- 4.6. 什么是 Promise.prototype.done ?
- 4.7. Promise和方法鏈(method chain)
- 4.8. 使用Promise進行順序(sequence)處理
- 第五章 - Promises API Reference
- 5.1. Promise#then
- 5.2. Promise#catch
- 5.3. Promise.resolve
- 5.4. Promise.reject
- 5.5. Promise.all
- 5.6. Promise.race
- 第六章 - 用語集
- 第七章 - 參考網站
- 第八章 - 關于作者
- 第九章 - 關于譯者