这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战
二、Promise 进阶
2.1 Promise 相互依赖
const setDelay = (millisecond) => {
return new Promise((resolve, reject) => {
if (typeof millisecond != 'number') {
reject(new Error('参数必须是numbe类型'));
}
setTimeout(() => {
resolve(`我延迟了${millisecond}毫秒后输出的`);
}, millisecond);
});
}
const setDelaySecond = (seconds) => {
return new Promise((resolve, reject)=>{
if (typeof seconds != 'number' || seconds > 10) {
reject(new Error('参数必须是number类型,并且于等于10'));
}
setTimeout(()=> {
console.log(`先是setDelaySeconds函数输出,延迟了${seconds}秒,一共需要延迟${seconds+2}秒`);
resolve(setDelay(2000)) // 这里依赖上一个Promise
}, seconds * 1000);
});
}
setDelaySecond(3).then((result)=>{
console.log(result);
// “先是setDelaySeconds输出,延迟了2秒,一共需要延迟5秒”
// “我延迟了2000毫秒后输出的”
}).catch((err)=>{
console.log(err);
})
这里做到了依次执行的目的,但如果不想耦合性这么高,想先执行 setDelay
函数再执行 setDelaySecond
也可以。
2.2 Promise 链式写法
先改写一下 setDelaySecond
,拒绝依赖,降低耦合性:
const setDelaySecond = (seconds) => {
return new Promise((resolve, reject)=>{
if (typeof seconds != 'number' || seconds > 10) {
reject(new Error('参数必须是number类型,并且于等于10'));
}
setTimeout(()=> {
resolve(`我延迟了${seconds}秒后输出的,是第二个函数`);
}, seconds * 1000);
})
}
先执行 setDelay
在执行 setDelaySecond
,只需要在第一个 then
的结果中返回下一个 Promise
就可以一直链式写下去了,相当于依次执行:
setDelay(2000)
.then((result)=>{
console.log(result)
console.log('我进行到第一步的');
return setDelaySecond(3)
})
.then((result)=>{
console.log('我进行到第二步的');
console.log(result);
}).catch((err)=>{
console.log(err);
})
// 我延迟了2000毫秒后输出的
// 我进行到第一步的
// 我进行到第二步的
// 我延迟了3秒后输出的,是第二个函数
Promise 链式写法的本质其实是一直往下传递返回一个新的 Promise,也就是说 then 在下一步接收的是上一步返回的 Promise。
2.3 Promise 错误处理
then 的其实有 2 个回调参数:
- 第一个回调是 resolve 的回调,也就是第一个参数用得最多,拿到的是上一步的 Promise 成功 resolve 的值。
- 第二个回调是 reject 的回调,用的不多,通常是拿到上一个的错误,那么这个错误处理和 catch 有什么区别和需要注意的地方呢?
setDelay(2000)
.then((result)=>{
console.log(result)
console.log('我进行到第一步的');
return setDelaySecond(20)
})
.then((result)=>{
console.log('我进行到第二步的');
console.log(result);
}, (_err)=> {
console.log('我出错啦,进到这里捕获错误,但是不经过catch了');
})
.then((result)=>{
console.log('我还是继续执行的!!!!')
})
.catch((err)=>{
console.log(err);
})
可以看到输出结果是:进到了 then 的第二个参数(reject)中去了,而且不再经过 catch 了
。
setDelay(2000)
.then((result)=>{
console.log(result)
console.log('我进行到第一步的');
return setDelaySecond(20)
})
.catch((err)=>{ // 挪上去了
console.log(err); // 这里 catch 到上一个返回 Promise 的错误
})
.then((result)=>{
console.log('我进行到第二步的');
console.log(result);
}, (_err)=> {
console.log('我出错啦,但是由于catch在我前面,所以错误早就被捕获了,我这没有错误了');
})
.then((result)=>{
console.log('我还是继续执行的!!!!')
})
可以看到先经过 catch 的捕获,后面就没错误了。
注意:
- catch 写法是针对于整个链式写法的错误捕获的,而 then 第二个参数是针对于上一个返回 Promise 的。
- 两者的优先级:就是看谁在链式写法的前面,在前面的先捕获到错误,后面就没有错误可以捕获了,链式前面的优先级大,而且两者都不是 break, 可以继续执行后续操作不受影响。
既然有了很多的 Promise,那么我需不需要写很多 catch 呢?
不需要!只需要在末尾 catch 一下就可以了,因为链式写法的错误处理具有“冒泡”特性,链式中任何一个环节出问题,都会被 catch 到,同时在某个环节后面的代码就不会执行了。
setDelay('2000')
.then((result)=>{
console.log('第一步完成了');
console.log(result)
return setDelaySecond(3)
})
.catch((err)=>{
// 这里移到第一个链式去,发现上面的不执行了,下面的继续执行
console.log(err);
})
.then((result)=>{
console.log('第二步完成了');
console.log(result);
})
链式中的 catch 并不是终点,catch 完如果还有 then 还会继续往下执行。