异步函数 | Promise值穿透

回顾一下红宝书第四版P330中的一句话:”传给then()的任何非函数类型的参数都会被静默忽略“,这个忽略是单纯的忽略吗?其实不然,应该考虑到值穿透的问题

.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透

举例1:

1
2
3
4
5
6
Promise.resolve(1)
.then(2) // 注意这里
.then(Promise.resolve(3))
.then(console.log)

// 打印结果为1

举例2:

1
2
3
4
5
6
7
Promise.resolve('foo')
.then(Promise.resolve('bar')) // 注意这里
.then(function(res) {
console.log(res);
})

// 打印结果为foo

举例3:

1
2
3
4
5
6
Promise.resolve(1)
.then(function() {return 2})
.then(Promise.resolve(3)) // 注意这里
.then(console.log)

// 打印结果为2

举例4:

1
2
3
4
5
6
7
8
Promise.resolve(1)
.then(function() {return 2})
.then(function() {return Promise.resolve(3)})
.then(console.log)

// 输出3

// 这个不是穿透的例子,主要是为了要理解:.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透

拓展:catch的穿透

举例1:一个很容易理解的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
new Promise((resolve, reject) => {
reject(1)
}) // 返回的Promise实例: Promise {<rejected>:1}
.then(value => {
console.log('success', value); // 这里不起作用因为这个是onResolved处理程序
}, reason => {
console.log('failture', reason); // 打印结果为:filture 1
}) // 返回的Promise实例: Promise {<resolved>:undefined} 因为没有显示的返回语句
.then(value => {
console.log('success', value); // 打印结果为:success
}, reason => {
console.log('failture', reason); // 不起作用
}) // 返回的Promise实例: Promise {<resolved>:undefined} 因为没有显示的返回语句

举例2:增加catch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
new Promise((resolve, reject) => {
reject(1)
}) // 返回的Promise实例: Promise {<rejected>:1}
.then(value => {
console.log('success', value); // 这里不起作用因为这个是onResolved处理程序
}, reason => {
console.log('failture', reason); // 打印结果为:filture 1
}) // 返回的Promise实例: Promise {<resolved>:undefined} 因为没有显示的返回语句
.then(value => {
console.log('success', value); // 打印结果为:success
}, reason => {
console.log('failture', reason); // 不起作用
}) // 返回的Promise实例: Promise {<resolved>:undefined} 因为没有显示的返回语句
.catch(reason => {
console.log('failture', reason); // 不会走到这里的,因为Promise实例的状态为resolved
})

举例3:then里面没有onRejected处理程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
new Promise((resolve, reject) => {
reject(1);
}) // 返回的Promise实例: Promise {<rejected>:1}
.then(value => {
console.log('success', value); //没有指定失败的回调函数,不执行代码,去往下一级寻找失败状态回调函数
})
.then(value => {
console.log('success', value); //没有指定失败的回调函数,不执行代码,去往下一级寻找失败状态回调函数
})
.catch(reason => {
console.log('failture', reason); // 打印结果为: failture 3
})

/*
当.then方法中没有指定失败的回调函数时
使用.catch会默认为没有指定失败回调函数的.then指定失败的回调函数
reason => {
throw reason // 注意这里不是return reason 而是throw reason throw保证了返回结果为失败
}
*/

举例4:在举例3上拓展,注意看变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
new Promise((resolve, reject) => {
reject(1);
}) // 返回的Promise实例: Promise {<rejected>:1}
.then(value => {
console.log('success', value); //没有指定失败的回调函数,不执行代码,去往下一级寻找失败状态回调函数
})
.then(value => {
console.log('success', value);
}, reason => { // 指定了失败的回调函数,执行失败的回调函数,而不会执行catch
console.log('failture111', reason); // 打印结果为: failture111 3
}) // 返回的Promise实例: Promise {<resolved>:undefined}
.catch(reason => {
console.log('failture', reason);
})

举例5:当一个promise是reject状态,但是没有失败回调,也没有写catch捕获,那么系统会默认捕获

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
new Promise((resolve, reject) => {
reject(1);
}) // 返回的Promise实例: Promise {<rejected>:1}
.then(value => {
console.log('success', value); //没有指定失败的回调函数,不执行代码,去往下一级寻找失败状态回调函数
})
.then(value => {
console.log('success', value); //没有指定失败的回调函数,不执行代码,去往下一级寻找失败状态回调函数
})

/*
系统默认捕获的形式如下
reason => {
throw reason // 注意这里不是return reason 而是throw reason throw保证了返回结果为失败
}
*/

等价代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
new Promise((resolve, reject) => {
reject(1);
}) // 返回的Promise实例: Promise {<rejected>:1}
.then(value => {
console.log('success', value); //没有指定失败的回调函数,不执行代码,去往下一级寻找失败状态回调函数
})
.then(value => {
console.log('success', value); //没有指定失败的回调函数,不执行代码,去往下一级寻找失败状态回调函数
})
.catch(reason => {throw reason});

/*
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "1".] {
code: 'ERR_UNHANDLED_REJECTION'
}
*/

总结

当使用.catch时,会默认为没有指定失败回调函数的.then添加一个失败回调函数【举例3,4】

.catch所谓的值穿透并不是一个失败状态就触发.catch,而是一层层传递下来的【举例3,5】

异常穿透的前提是所有.then都没有执行失败状态的回调函数【举例3,5】

如果.catch前所有的.then都制定了失败状态的回调函数,.catch就失去了意义【举例2,4】

参考

Promise 值穿透 特性