2

一、处于then链中Promise对象的状态跟其前一个Promise对象的状态没有直接关系

即:var promise2 = promise1.then(onFulfilled, onRejected)表达式中promise2的状态跟promise1没有直接关系。

// 创建promise1对象
var promise1 = new Promise((resolve, reject) => {
    reject('Rejected')
})

// 创建promise2对象,来自`promise1.then`的返回值
var promise2 = promise1.then(value => {
    console.log(`[promise1]: fulfilled, value='${value}'`)
}, reason => {
    console.log(`[promise1]: rejected, reason='${reason}'`)
})

promise2.then(value => {
    console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
    console.log(`[promise2]: rejected, reason='${reason}'`)
})

输出:

[promise1]: rejected, reason='Rejected'
[promise2]: fulfilled, value='undefined'

promise1变成了rejected,但是 promise2却是fulfilled状态。

1.2 分析promise2 = promise1.then(onFulfilled, onRejected)

记住两点:

  1. promise1的终态只是决定调用onFulfilled还是onFulfilled回调函数。
  2. promise2的终态只是受onFulfilled OR onRejected回调函数影响:
  • 有无onFulfilled/onRejected回调函数;
  • onFulfilled/onRejected回调函数执行中是否发生异常;
  • onFulfilled/onRejected回调函数的返回值x

1. 如果没有onFulfilled/onRejected回调函数:

如promise1是fulfilledrejected)且没有onFulfilledonRejected)回调函数,,此时promise2采用promise1的状态(即promise2的状态跟promise1的状态一致):

var promise1 = new Promise((resolve, reject) => {
    reject('Rejected')
})
// promise1没有注册`onRejected`回调函数
var promise2 = promise1.then()

promise2.then(value => {
    console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
    console.log(`[promise2]: rejected, reason='${reason}'`)
})

输出:

[promise2]: rejected, reason='Rejected'
var promise1 = new Promise((resolve, reject) => {
    resolve('Fulfilled')
})
// promise1没有注册`onFulfilled`回调函数
var promise2 = promise1.then()

promise2.then(value => {
    console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
    console.log(`[promise2]: rejected, reason='${reason}'`)
})

输出:

[promise2]: fulfilled, value='Fulfilled'

2. 如果 onFulfilled/onRejected执行过程中抛出了异常:

此时Reject promise2对象,并且把异常作为reason。

var promise1 = new Promise((resolve, reject) => {
    resolve('Fulfilled')
})

// promise1的`onFulfilled`回调函数抛出了异常
var promise2 = promise1.then(() => {
    throw new Error('A Error in onFulfilled func')
})

promise2.then(value => {
    console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
    console.log(`[promise2]: rejected, reason='${reason}'`)
})

输出:

[promise2]: rejected, reason='Error: A Error in onFulfilled func'

3. 如果onFulfilled/onRejected函数的返回值是x

如果onFulfilled/onRejected函数正常执行完,且返回值是x(如果没有显示指定返回值,则x为undefined)。

3.1 如果x也是个Promise对象,则promise2采用x的状态:
var promise1 = new Promise((resolve, reject) => {
    resolve('Fulfilled')
})

// promise1的`onFulfilled`回调函数返回`promise1`对象
var promise2 = promise1.then(() => {
    return promise1;
})

promise2.then(value => {
    console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
    console.log(`[promise2]: rejected, reason='${reason}'`)
})

输出:

[promise2]: fulfilled, value='Fulfilled

此时promise2promise1具有相同的状态和相同的value。

3.2 如果xpromise2相等,则抛出TypeError异常:

上个规则成立的前提就是xpromise2不能相等,否则就会产生互相依赖的矛盾。
如果xpromise2相等,则抛出TypeError异常,并且作为promise2对象rejected状态的reason。

var promise1 = new Promise((resolve, reject) => {
    resolve('Fulfilled')
})

// promise1的`onFulfilled`回调函数返回了`promise2`
var promise2 = promise1.then(() => {
    return promise2;
})

promise2.then(value => {
    console.log(`[promise2]: fulfilled, value='${value}'`)
}, reason => {
    console.log(`[promise2]: rejected, reason='${reason}'`)
})

输出:

[promise2]: rejected, reason='TypeError: Chaining cycle detected for promise #<Promise>'
3.3 如果x是个thenable对象:

则执行大致下面的流程:

var then = x.then;
then.call(x, resolve, reject);

这个过程也称为PromiseResolveThenableJob

  1. 如果x.then内部先调用了resolve(y),则进行[[Resolve]](promise, y)
    此时会开启新的Promise解析过程,y千万不能和x相等,否则会产生无限递归。
  2. 如果x.then内部先调用了rejected(y), 则promise2也变成rejected,并且把y作为reason;
  3. 如果x.then内部先抛了异常, 则promise2也变成rejected,并且把异常作为reason;

别以为thenable对象不常见,其实Promise构造函数的实参函数就是遵循thenable对象的then方法规则。

var thenableA = {
    name: 'thenableA',
    then: function (resolve, reject) {
        console.log(`I'am ${this.name}`);
        resolve(this.name)
    }
}

new Promise((resolve, reject) => {
    console.log('create promise1');
    resolve(thenableA)
}).then(() => {
    console.log('promise1 fulfilled');
})

// promise1的`onFulfilled`回调函数返回thenableA
new Promise(resolve => {
    console.log('create promise2');
    resolve();
}).then(() => {
     console.log('promise2 fulfilled');
})

输出:

create promise1
create promise2
I'am thenableA
promise2 fulfilled
promise1 fulfilled

注意:promise2 fulfilled要先于promise1 fulfilled,这是因为PromiseResolveThenableJob过程需要作为一个 job 加入微任务队列,以保证对 then 方法的解析发生在其他上下文代码的解析之后。

3.4 其他情况(即x不是Promise对象,也不是thenable对象):

promise2变成fulfilled,并且把x作为其value。

  1. 如同开头的DEMO中,即使promise1变成了rejected,但是promise2却是fulfilled状态。

因为promise1onRejected回调函数的返回值xundefined所以promise2的value是undefined

  1. 如果要透传x,则必须显示地onFulfilled函数里显示的返回x

二、promsie.finally(onFinally)不等价于 promsie.then(onFinally, onFinally)

无论Promise处于什么终态都会触发onFinally回调函数,但是promsie.finally(onFinally)不等价于 promsie.then(onFinally, onFinally)

// Resolve
var promise2 = Promise.resolve(2)
.then(() => {});

promise2.then( value => {
    console.log(`Fulfilled, value=${value}`) // value是undefined
})

var promise2 = Promise.resolve(2)
.finally( () => {});

promise2.then( value => {
    console.log(`Fulfilled, value=${value}`) // value是2
})

// Reject
var promise2 = Promise.reject(2)
.then(() => {}, () => {})

// promise2 依旧是fulfilled
promise2 .then(value => {
    console.log(`Fulfilled, value=${value}`) // // value是undefined
}, reason => {
    console.log(`Rejected, reason=${reason}`)
})

var promise2 = Promise.reject(2).finally(() => {}, () => {});

// promise2也变成rejected
promise2 .then(value => {
    console.log(`Fulfilled, value=${value}`)
}, reason => {
    console.log(`Rejected, reason=${reason}`) // reason是2
})
  1. onFinally回调函数没有参数,因为它不知道Promise的终态是fulfilled还是rejected;
  2. finally方法的原则是不改变then链的执行过程(除非finally方法内部抛出异常),所以看到上面的例子中finally方法就像不存在前后then链里一样。

    Promise.reject('failed')
    .finally(() => {
        console.log('handle in finally')
        return 'Hi i am finally'; // 显示的返回个value,但是返回的Promise对象依旧是rejected
    })
    .then(value => {
        console.log(`Fullfilled: handle in then value=${value}`)
    }, reason => {
        console.log(`Rejected: handle in then reason=${reason}`) // 
    })
  3. 如果finally的回调函数返回的是个Promise,则也是等这个Promise对象进入终态后才执行后面的then链。

    Promise.reject('failed')
    .finally(() => {
        console.log('handle in finally')
        console.log('waiting 3s ...')
        return new Promise(resolve => { // 3s后再执行后面的
            setTimeout(resolve, 3000) // 虽然fullfilled,但是finally方法返回的Promise对象依旧rejected
        })
    })
    .then(value => {
        console.log(`Fullfilled: handle in then value=${value}`)
    }, reason => {
        console.log(`Rejected: handle in then reason=${reason}`)
    })
  4. 如果finally的回调函数抛出异常,或则返回个rejected的Promise,则会替换前面Promise的终态value或则reason

    Promise.reject('failed')
    .finally(() => {
        console.log('handle in finally')
        console.log('waiting 3s ...')
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                reject('reason from finally callback ')
            }, 3000)
        })
    })
    .then(value => {
        console.log(`Fullfilled: handle in then value=${value}`)
    }, reason => {
        console.log(`Rejected: handle in then reason=${reason}`)
    })

    输出:

    handle in finally
    waiting 3s ...
    Rejected: handle in then reason=reason from finally callback
  5. finally的行为好奇葩,最后还是把它写在then链的最后,以防阅读代码障碍。
  6. finally方法遵循的原则就是透传上一个Promise对象的结果,但是如果发生错误,则告诉后面的then链最新的错误。

2.2 实现Promise.prototype.finally(onFinally)

Promise.prototype.finally = function(onFinally) {    
    return this.then(value => {
        return Promise.resolve(onFinally())
            .then(
            () => value, // 透传上个Promise对象的终态`value`
            reason => {
                throw reason // 如果拒绝,则使用新的`reason`
            });
        }, reason => {
           return Promise.resolve(onFinally())
               .then(
               () => {
                   throw reason // 透传上个Promise对象的终态`reason`
               }, 
               reason => {
                    throw reason // 如果拒绝,则使用新的`reason`
               });
        })
}

因为onRejected回调都是透传新的reason, 故省去onRejected回调函数,即可以简化写法:

Promise.prototype.finally = function(onFinally) {    
    return this.then(value => {
        return Promise.resolve(onFinally())
            .then(() => value) // 透传上个Promise对象的终态`value`;
        }, reason => {
           return Promise.resolve(onFinally())
               .then(
               () => {
                   throw reason // 透传上个Promise对象的终态`reason`
               });
        })
}

三、Promise.resolve(value)不等价new Promise(resolve => resolve(value))

new Promise(resolve => {
    resolve();
})
.then(() => {
    console.log(1)
})

Promise.resolve()
.then(() => {
    console.log(2)
})

输出结果:1 -> 2,把value改成个Promise对象试试:

var p = new Promise(resolve => {
    resolve();
});

new Promise(resolve => {
    resolve(p);
})
.then(() => {
    console.log(1) // 回调函数会被延迟一个EventLoop
})

Promise.resolve(p)
.then(() => {
    console.log(2)
})

输出结果居然变了。

  1. Promise.resolve(value)中,如果实参value是个Promise对象(不包含thenable对象),则直接返回实参。
  2. new Promise(resolve => resolve(value))中如果value是个Promise或则thenable对象时,则会产生PromiseResolveThenableJob,从而导致增加一个异步job。

参考

  1. 整理自gitHub笔记:JS-ES6-Promise/Promise A+规范

普拉斯强
2.7k 声望53 粉丝

Coder