這里我們來介紹一下如何編寫Promise代碼。
## 1.3.1\. 創建promise對象
創建promise對象的流程如下所示。
1. `new Promise(fn)`?返回一個promise對象
2. 在`fn`?中指定異步等處理
* 處理結果正常的話,調用`resolve(處理結果值)`
* 處理結果錯誤的話,調用`reject(Error對象)`
按這個流程我們來實際編寫下promise代碼吧。
我們的任務是用Promise來通過異步處理方式來獲取XMLHttpRequest(XHR)的數據。
### 創建XHR的promise對象
首先,創建一個用Promise把XHR處理包裝起來的名為?`getURL`?的函數。
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(function onRejected(error){
console.error(error);
});
~~~
`getURL`?只有在通過XHR取得結果狀態為200時才會調用?`resolve`?- 也就是只有數據取得成功時,而其他情況(取得失敗)時則會調用?`reject`?方法。
`resolve(req.responseText)`?在response的內容中加入了參數。 resolve方法的參數并沒有特別的規則,基本上把要傳給回調函數參數放進去就可以了。 (?`then`?方法可以接收到這個參數值)
熟悉Node.js的人,經常會在寫回調函數時將?`callback(error, response)`?的第一個參數設為error對象,而在Promise中resolve/reject則擔當了這個職責(處理正常和異常的情況),所以 在resolve方法中只傳一個response參數是沒有問題的。
接下來我們來看一下`reject`函數。
XHR中?`onerror`?事件被觸發的時候就是發生錯誤時,所以理所當然調用`reject`。 這里我們重點來看一下傳給`reject`的值。
發生錯誤時要像這樣?`reject(new Error(req.statusText));`?,創建一個Error對象后再將具體的值傳進去。 傳給`reject`?的參數也沒有什么特殊的限制,一般只要是Error對象(或者繼承自Error對象)就可以。
傳給`reject`?的參數,其中一般是包含了reject原因的Error對象。 本次因為狀態值不等于200而被reject,所以`reject`?中放入的是statusText。 (這個參數的值可以被?`then`?方法的第二個參數或者?`catch`?方法中使用)
## 1.3.2\. 編寫promise對象處理方法
讓我們在實際中使用一下剛才創建的返回promise對象的函數
~~~
getURL("http://example.com/"); // => 返回promise對象
~~~
> 如[Promises Overview](http://liubin.github.io/promises-book/#promises-overview)?中做的簡單介紹一樣,promise對象擁有幾個實例方法, 我們使用這些實例方法來為promise對象創建依賴于promise的具體狀態、并且只會被執行一次的回調函數。
為promise對象添加處理方法主要有以下兩種
* promise對象被?**resolve**?時的處理(onFulfilled)
* promise對象被?**reject**?時的處理(onRejected)

Figure 2\. promise value flow
首先,我們來嘗試一下為?`getURL`?通信成功并取到值時添加的處理函數。
此時所謂的?_通信成功_?, 指的就是在被resolve后,?_promise對象變為FulFilled狀態_?。
被**resolve**后的處理,可以在[`.then`](http://liubin.github.io/promises-book/#promise.then)?方法中傳入想要調用的函數。
~~~
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){ //為了方便理解我們把函數命名為?`onFulfilled`
console.log(value);
});
~~~
[getURL函數](http://liubin.github.io/promises-book/#xhr-promise.js)?中的?`resolve(req.responseText);`?會將promise對象變為resolve(Fulfilled)狀態, 同時使用其值調用?`onFulfilled`?函數。
不過目前我們還沒有對其中可能發生的錯誤做任何處理, 接下來,我們就來為?`getURL`?函數添加發生錯誤時的異常處理。
此時?_發生錯誤_?, 指的也就是reject后?_promise對象變為Rejected狀態_?。
被**reject**后的處理,可以在[`.then` 的第二個參數](http://liubin.github.io/promises-book/#promise.then)?或者是在?[`.catch`](http://liubin.github.io/promises-book/#promise.catch)?方法中設置想要調用的函數。
把下面reject時的處理加入到剛才的代碼,如下所示。
~~~
var URL = "http://httpbin.org/status/500"; //服務端返回的狀態碼為500
getURL(URL).then(function onFulfilled(value){
console.log(value);
}).catch(function onRejected(error){ // 為了方便理解函數被命名為?`onRejected`
console.error(error);
});
~~~
在`getURL`?的處理中發生任何異常,或者被明確reject的情況下, 該異常原因(Error對象)會作為?[`.catch`](http://liubin.github.io/promises-book/#promise.catch)?方法的參數被調用。
其實?[`.catch`](http://liubin.github.io/promises-book/#promise.catch)只是?`promise.then(undefined, onRejected)`?的別名而已, 如下代碼也可以完成同樣的功能。
~~~
getURL(URL).then(onFulfilled, onRejected);//onFulfilled, onRejected 是和剛才相同的函數
~~~
一般說來,使用[`.catch`](http://liubin.github.io/promises-book/#promise.catch)來將resolve和reject處理分開來寫是比較推薦的做法, 這兩者的區別會在[then和catch的區別](http://liubin.github.io/promises-book/#then-or-catch)中再做詳細介紹。
## 總結
在本章我們簡單介紹了以下內容:
* 用?`new Promise`?方法創建promise對象
* 用[`.then`](http://liubin.github.io/promises-book/#promise.then)?或?[`.catch`](http://liubin.github.io/promises-book/#promise.catch)?添加promise對象的處理函數
到此為止我們已經學習了Promise的基本寫法。 其他很多處理都是由此基本語法延伸的,也使用了Promise提供的一些靜態方法來實現。
實際上即使使用回調方式的寫法也能完成上面同樣的工作,而使用Promise方式的話有什么優點么?在本小節中我們沒有講到兩者的對比及Promise的優點。在接下來的章節中,我們將會對Promise優點之一,即錯誤處理機制進行介紹,以及和傳統的回調方式的對比。
- 前言
- 第一章 - 什么是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
- 第六章 - 用語集
- 第七章 - 參考網站
- 第八章 - 關于作者
- 第九章 - 關于譯者