7

前言

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

链式调用

在jQuery的使用中,我们常常使用下面的代码

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

这是因为jQuery的对象在调用上述方法的时候,会return此对象本身, 以方便后面可以继续调用此对象的方法。

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

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

Promise的链式调用

Promise是支持链式调用的,但是它是不同于上面jQuery的链式。jQeury是调用方法返回自身,但是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实例对象的状态。

下图以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没有成功添加回调函数,发生了值穿透。

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。

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的状态又只与它们所注册的回调方法的执行相关。不会受到前面的影响。


火星田园犬
933 声望685 粉丝

小心驾驶, 专业埋雷