Promise学习笔记(二)

186 阅读3分钟

链式调用

Promise的.then方法会返回一个新的Promise(catch同理),所以可以将then链接起来。

它的值和状态都取决于上一个then的执行情况

1、上一个then没有返回值

新promise实例状态为fulfilled,值为undefined

let p1 = new Promise((resolve,reject) => {
    resolve(1);
})

let p2 = p1.then((value) => {
    console.log('xxx');
})
// p1完成后,p2:{<fulfilled>: undefined}

2、上一个then返回非promise的任意值

新promise实例状态为fulfilled,值为返回值

let p1 = new Promise((resolve,reject) => {
    resolve(2);
})

let p2 = p1.then((value) => {
    return 6;
})
// p1完成后,p2:{<fulfilled>: 6}

3、上一个then显式地返回一个promise

新promise实例的结果等于返回的promise的结果

let p1 = new Promise((resolve,reject) => {
    resolve(3);
})

let p2 = p1.then((value) => {
    return Promise.resolve(9);
})
// p1完成后,p2:{<fulfilled>: 9}

4、上一个then没有提供onResolved或者onRejected方法

如果上一个promise实例是成功的,而then没有提供onResolved方法,就基于上一次resolve的结果

let p1 = new Promise((resolve,reject) => {
    resolve(2);
})

let p2 = p1.then();
// p1完成后,p2:{<fulfilled>: 2}

5、上一个then中抛出错误

新promise实例状态为rejected,值为抛出的异常

let p1 = new Promise((resolve,reject) => {
    resolve(2)
})

let p2 = p1.then((value) => {
	throw new Error('fail');
})
// p1完成后,p2:{<rejected>: 'fail'}

中断promise链

如果在某一步后,不想再执行后面的then,从此中断promise链,可以返回一个pending状态的promise:

new Promise((resolve, reject) => {
    resolve(100)
}).then(value => {
    
}).then(value => {
    //到此终止
    return new Promise(() => {})
}).then(value => {
    
}).catch(reason => {
    console.log(reason)
})

这样写的话,最后一个then和最后一个catch都不会执行(因为无法接收到 fulfilled 或者 rejected 的promise实例)

错误处理

前面已经说过了,Promise中的错误处理,拒绝处理函数会被传递到then函数的第二个参数,或者用catch来捕捉。如果then中的回调函数执行出错,那么这个then的返回一个rejected状态的Promise,如:

var p = Promise.resolve(42);

p.then((msg) => {
    console.log(msg.toLowerCase());
}, (err) => {
    console.log(err); // 永远不会执行
})

p的状态是fulfilled,返回值42,但是在起成功处理函数中,number类型没有toLowerCase方法,所以会抛出一个错误。

但为什么这个错误不会被我们的错误处理函数捕获呢?

因为p这个promise已经用值42填充了,变成了fulfiiled状态,它不会再被改变。所以p.then(...)里面的错误,会被通知到p.then(...).then(...)中,但是我们没有在这里捕捉。

为了避免丢失被忽略或抛弃的Promise错误,一些开发者表示,最佳实践是在最后总以一个catch(..)结束,这种处理叫异常穿透

如果第一个开头的promise失败了,但是后面的then都没有写onRejected函数:

new Promise((resolve, reject) => {
    reject(1);
}).then(value => {
    
}).then(value => {
    
}).then(value => {
    
}).catch(reason => {
    console.log(reason);
})

那么这个reject(1)就会一层一层透传到最后一个catch(注意:它不是一下子找到最后一个catch的)

无论是then还是catch,它的执行情况都取决于上一个then()

因为then不手动写onRejected函数,默认被这样处理:

.then(value => {
    
}, reason => {
	throw reason;
})

但是不管怎样,在Promise链的最后一步,总存在着未捕获的错误的可能性,尽管这种可能性越来越低。

有没有办法解决这个问题呢?

有一些Promise库增加了一些方法,用于注册一个类似“全局未处理拒绝”的处理函数,这样就不会抛出全局错误,而是调用这个函数。

window.addEventListener("unhandledrejection", handler);

有一种看法是:Promise应该添加一个done函数,从本质上标识标识Promise链的结束。done的回调中抛出的错误,会被当作一个全局未处理的错误,可以在try...catch块中捕获到。

然而,它并不是ES6标准的一部分,我们可以选择自己实现它:

Promise.prototype.done = function (onFulfilled, onRejected) {
    this
        .then(onFulfilled, onRejected)
        .catch(function (reason) {
        // 抛出一个全局错误
        setTimeout(() => {
            throw reason
        }, 0);
    })
}