深刻学习Promise调用链

前言

使用Promise中,链式的调用对于控制异步执行很重要。css

链式调用

在jQuery的使用中,咱们经常使用下面的代码node

$('#app').show().css("color","red");

这是由于jQuery的对象在调用上述方法的时候,会return此对象自己, 以方便后面能够继续调用此对象的方法。segmentfault

jQuery.fn.css = function(prop, value) {
    ......
    return this;
}

jQuery.fn.show = function() {
    ......
    return this;
}

Promise的链式调用

Promise是支持链式调用的,可是它是不一样于上面jQuery的链式。jQeury是调用方法返回自身,可是Promise是调用方法后返回一个新的Promise。promise

const promise = new Promise((resolve, reject) => {
    resolve('ok');
})

const promise$1 = promise.then(() => {console.log()});

promise$1 === promise // false

能够看到上面的promise$1是不等于promise的,若是能够在node.js 或者在浏览器中进行断点调试的话,还能看到promise$1的初始化状态是pending的。
clipboard.png浏览器

当Promise的实例使用then, catch, finally添加完回调方法之后,会返回一个初始化状态为pending的Promise的实例对象。此对象的状态咱们在外部的程序是没法进行改变的,它的状态取决于前面所注册的回调方法的执行状况。 当回调方法运行正常,没有产生错误或者异常,返回的值除Promise实例与自己(返回自己将会报错),返回的Promise的实例对象状态会变成resolved, 若是有错误或者异常,则会把状态变为rejected。固然了返回值是Promise的实例对象,那么次Promise实例对象的状态取决于返回的Promise实例对象的状态。 app

下图以then为例展现promise链式调用运行的流程异步

clipboard.png

示例分析

1. 调用链then的执行顺序
const promise$0 = Promise.resolve('resolve_0');
const promise$1 = new Promise((resolve, reject) => {resolve('resolve_1')})
promise$0.then((val) => { console.log(val) }).then(() => { console.log('continue') });
promise$1.then((val) => { console.log(val) });       
       
//输出结果: resolve_0  resolve_1  continue

promise$0与promise$1在使用then添加回掉函数以前,状态已经从pending变为resolved,它们添加的回掉函数会被当即添加到Promise的运行队列,promise$0.then((val) => { console.log(val) })返回的Promise实例须要等待所注册的回调函数成功执行完毕之后,此Promise的状态才从pending变为resolved。 Promise的运行机制请参考: Promise的运行机制函数

2. 值穿透
const promise = Promise.resolve('ok');

promise.then().then((val) => {console.log(val)});

// ok

因为promise经过then没有成功添加回调函数,发生了值穿透。this

3. 状态传递
const promise = Promise.resolve('complete');

promise.then((val) => { 
    return new Promise ((resolve) => { 
             resolve(val)
            console.log(val, 1); 
        })
    })
    .then((val) => {console.log(val, 2)})
// complete 1 complete 2

咱们把Promise暂命名(Pa), 经过promise.then返回的Promise(暂命名Pb), Pb状态须要根据Pathen所注册回调方法的运行,其回调方法返回一个新的Promise(暂命名Pc),因为返回的是Promise。Pb的状态变成取决于Pc的状态, 当Pc的状态变化为resolved之后,Pb的状态也变化为resolved。spa

4. 巧妙的异常处理
var promise = Promise.resolve('ok');

promise.then(() => {
    throw new Error('error');
}).then(() => {
    console.log('continue');
}).then(() => {
    console.log('again');
}).catch(() => {

}).then(() => {
  console.log('completed')
})
// completed

在then所注册的回调方法,发生异常之后,后续的Promise调用链的状态都是rejected,致使后续的then(resolve)不会被推入Promise的运行队列,也就不会被运行,直到这个错误被then(,reject) 或者catch捕获。 而使用(then(,reject)) 或者catch注册回调方法又会返回一个新的Promise,此Promise的状态又只与它们所注册的回调方法的执行相关。不会受到前面的影响。