在前面的章節里我們對Promise基本的實例方法?`then`?和?`catch`?的使用方法進行了說明。
這其中,我想大家已經認識了?`.then().catch()`?這種鏈式方法的寫法了,其實在Promise里可以將任意個方法連在一起作為一個方法鏈(method chain)。
promise可以寫成方法鏈的形式
~~~
aPromise.then(function taskA(value){
// task A
}).then(function taskB(vaue){
// task B
}).catch(function onRejected(error){
console.log(error);
});
~~~
如果把在?`then`?中注冊的每個回調函數稱為task的話,那么我們就可以通過Promise方法鏈方式來編寫能以taskA → task B 這種流程進行處理的邏輯了。
Promise方法鏈這種叫法有點長(其實是在日語里有點長,中文還可以 --譯者注),因此后面我們會簡化為?[promise chain](http://liubin.github.io/promises-book/#promise-chain)?這種叫法。
Promise之所以適合編寫異步處理較多的應用,promise chain可以算得上是其中的一個原因吧。
在本小節,我們將主要針對使用?`then`?的promise chain的行為和流程進行學習。
## 2.4.1\. promise chain
在第一章?[promise chain](http://liubin.github.io/promises-book/#promise-chain)?里我們看到了一個很簡單的 then → catch 的例子,如果我們將方法鏈的長度變得更長的話,那在每個promise對象中注冊的onFulfilled和onRejected將會怎樣執行呢?
> promise chain - 即方法鏈越短越好。 在這個例子里我們是為了方便說明才選擇了較長的方法鏈。
我們先來看看下面這樣的promise chain。
promise-then-catch-flow.js
~~~
function taskA() {
console.log("Task A");
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
~~~
上面代碼中的promise chain的執行流程,如果用一張圖來描述一下的話,像下面的圖那樣。

Figure 3\. promise-then-catch-flow.js附圖
在?[上述代碼](http://liubin.github.io/promises-book/#promise-then-catch-flow.js)?中,我們沒有為?`then`?方法指定第二個參數(onRejected),也可以像下面這樣來理解。
`then`
注冊onFulfilled時的回調函數
`catch`
注冊onRejected時的回調函數
再看一下?[上面的流程圖](http://liubin.github.io/promises-book/#promise-then-catch-flow.png)?的話,我們會發現?_Task A_?和?_Task B_?都有指向?_onRejected_?的線出來。
這些線的意思是在?_Task A_?或?_Task B_?的處理中,在下面的情況下就會調用?_onRejected_?方法。
* 發生異常的時候
* 返回了一個Rejected狀態的promise對象
在?[第一章](http://liubin.github.io/promises-book/#how-to-write-promise)?中我們已經看到,Promise中的處理習慣上都會采用?`try-catch`?的風格,當發生異常的時候,會被?`catch`?捕獲并被由在此函數注冊的回調函數進行錯誤處理。
另一種異常處理策略是通過?_返回一個Rejected狀態的promise對象_?來實現的,這種方法不通過使用?`throw`?就能在promise chain中對?`onRejected`?進行調用。
關于這種方法由于和本小節關系不大就不在這里詳述了,大家可以參考一下第4章?[使用reject而不是throw](http://liubin.github.io/promises-book/#not-throw-use-reject)?中的內容。
此外在promise chain中,由于在?_onRejected_?和?_Final Task_?后面沒有?`catch`?處理了,因此在這兩個Task中如果出現異常的話將不會被捕獲,這點需要注意一下。
下面我們再來看一個具體的關于?_Task A_?→?_onRejected_?的例子。
### Task A產生異常的例子
_Task A_?處理中發生異常的話,會按照TaskA → onRejected → FinalTask 這個流程來進行處理。

Figure 4\. Task A產生異常時的示意圖
將上面流程寫成代碼的話如下所示。
promise-then-taska-throw.js
~~~
function taskA() {
console.log("Task A");
throw new Error("throw Error @ Task A")
}
function taskB() {
console.log("Task B");// 不會被調用
}
function onRejected(error) {
console.log(error);// => "throw Error @ Task A"
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
~~~
執行這段代碼我們會發現?_Task B_?是不會被調用的。
> 在本例中我們在taskA中使用了?`throw`?方法故意制造了一個異常。但在實際中想主動進行onRejected調用的時候,應該返回一個Rejected狀態的promise對象。關于這種兩種方法的異同,請參考?[使用reject而不是throw](http://liubin.github.io/promises-book/#not-throw-use-reject)?中的講解。
## 2.4.2\. promise chain 中如何傳遞參數
前面例子中的Task都是相互獨立的,只是被簡單調用而已。
這時候如果 Task A 想給 Task B 傳遞一個參數該怎么辦呢?
答案非常簡單,那就是在 Task A 中?`return`?的返回值,會在 Task B 執行時傳給它。
我們還是先來看一個具體例子吧。
promise-then-passing-value.js
~~~
function doubleUp(value) {
return value * 2;
}
function increment(value) {
return value + 1;
}
function output(value) {
console.log(value);// => (1 + 1) * 2
}
var promise = Promise.resolve(1);
promise
.then(increment)
.then(doubleUp)
.then(output)
.catch(function(error){
// promise chain中出現異常的時候會被調用
console.error(error);
});
~~~
這段代碼的入口函數是?`Promise.resolve(1);`?,整體的promise chain執行流程如下所示。
1. `Promise.resolve(1);`?傳遞 1 給?`increment`?函數
2. 函數?`increment`?對接收的參數進行 +1 操作并返回(通過`return`)
3. 這時參數變為2,并再次傳給?`doubleUp`?函數
4. 最后在函數?`output`?中打印結果

Figure 5\. promise-then-passing-value.js示意圖
每個方法中?`return`?的值不僅只局限于字符串或者數值類型,也可以是對象或者promise對象等復雜類型。
return的值會由?`Promise.resolve(return的返回值);`?進行相應的包裝處理,因此不管回調函數中會返回一個什么樣的值,最終?`then`?的結果都是返回一個新創建的promise對象。
> 關于這部分內容可以參考?[專欄: 每次調用then都會返回一個新創建的promise對象](http://liubin.github.io/promises-book/#then-return-new-promise)?,那里也對一些常見錯誤進行了介紹。
也就是說,?`Promise#then`?不僅僅是注冊一個回調函數那么簡單,它還會將回調函數的返回值進行變換,創建并返回一個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
- 第六章 - 用語集
- 第七章 - 參考網站
- 第八章 - 關于作者
- 第九章 - 關于譯者