在前面的章节里我们对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的执行流程,如果用一张图来描述一下的话,像下面的图那样。
![promise-then-catch-flow](https://box.kancloud.cn/2015-07-20_55ac7253d44c0.png)
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 这个流程来进行处理。
![promise taska rejected flow](https://box.kancloud.cn/2015-07-20_55ac7254c58a4.png)
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` 中打印结果
![promise-then-passing-value](https://box.kancloud.cn/2015-07-20_55ac7259b3351.png)
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
- 第六章 - 用語集
- 第七章 - 参考网站
- 第八章 - 关于作者
- 第九章 - 关于译者