跳到主要内容

异常处理

2023年06月16日
柏拉文
越努力,越幸运

一、认识


Promise 的异常穿透机制: return new Promise(()=>{}) 以及 Promise.then(()=>{}) 回调函数内部发生的错误具有 冒泡 的性质, 会一直向后传递,直到被捕获为止。也就是说: 错误总是被下一个 catch 语句捕获。因此, Promise 的异常穿透有以下特点:

  1. Promise 回调函数内部任何操作出现问题, 不会传递到外层代码, 不会退出进程, 不会中断外部程序执行

  2. Promise 失败回调 获取不到自身 成功回调 抛出的错误, 因此, 我们可以在最后指定失败的回调

  3. 前面任何操作出现了异常, 都会直接传递到最后失败的回调中进行处理。会跳过 抛出错误失败回调 的中间环节。

  4. 在失败的回调处理完成后, 返回的还是 Promise , 因此可以在失败的回调后继续 .then 或者 .catch

二、吃掉错误


如果没有使用 .then(()=>{},()=>{}) 或者 .then(()=>{}).catch(()=>{}) 指定错误处理的回调函数, return new Promise(()=>{}) 以及 Promise.then(()=>{})抛出的错误不会传递到外层代码,即不会有任何反应。 也就是说即使 return new Promise(()=>{}) 以及 Promise.then(()=>{}) 回调函数内部抛出错误, 不会退出进程, 中断程序执行。通俗的说法就是 Promise会吃掉错误

console.log("失败之前");
new Promise((resolve) => {
throw new Error("失败");
})
.then(() => {
console.log(1);
})
.then(() => {
console.log(2);
})
.then(() => {
console.log(3);
})
.then(() => {
console.log(4);
});

console.log("失败之后");

三、自身错误


Promise 的错误总是被下一个 catch 语句捕获。因此, Promise 失败回调 获取不到自身 成功回调 抛出的错误, 因此, 我们可以在最后指定失败的回调。

3.1 .then(()=>,()=>)

.then(()=>{},()=>{}) 可以捕获之前 then 或者 new Promise(()=>{}) 中抛出的错误

return new Promise((resolve) => {
resolve();
})
.then(() => {
console.log(1);
})
.then(() => {
console.log(2);
throw new Error("失败");
})
.then(
() => {
console.log(3);
},
(error) => {
console.log("3 error", error);
}
);

.then(()=>{},()=>{}) 捕获不到自身 resolved 回调抛出的错误

return new Promise((resolve) => {
resolve();
})
.then(() => {
console.log(1);
})
.then(() => {
console.log(2);
})
.then(
() => {
console.log(3);
throw new Error("失败");
},
(error) => {
console.log("3 error", error);
}
);

这是因为 Promise 的错误穿透机制: 自身抛出的错误会一直向后传递, 总是被下一个 .then(()=>{},()=>{}) 或者 .catch() 函数捕获。

3.2 .then(()=>).catch(()=>)

一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。.then(()=>{}).catch(()=>{}) 写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch()方法,而不使用then()方法的第二个参数。

四、错误越级


Promise 前面任何操作包括(return new Promise(()=>{}) 以及 Promise.then(()=>{},()=>{}) 回调函数内部)出现了异常, 都会直接传递到最后失败的回调 包括(.then(()=>{},()=>{ 处理失败 }) 以及 .then().catch(()=>{}))中进行处理, 会跳过 抛出错误失败回调 的中间环节。

4.1 new Promie then then

new Promise((resolve) => {
throw new Error("失败");
})
.then(() => {
console.log(1); // 不执行
})
.then(() => {
console.log(2); // 不执行
});

4.2 new Promise then then rejectCallback or catch

new Promise then then catch

new Promise((resolve) => {
throw new Error("失败");
})
.then(() => {
console.log(1); // 不执行
})
.then(() => {
console.log(2); // 不执行
})
.catch((error) => {
console.log("catch error", error);
});

new Promise then then rejectCallback

new Promise((resolve) => {
throw new Error("失败");
})
.then(() => {
console.log(1); // 不执行
})
.then(
() => {
console.log(2); // 不执行
},
(error) => {
console.log("rejectCallback", error);
}
);

4.3 new Promise then then rejectCallback or catch then

new Promise then then catch then

new Promise((resolve) => {
throw new Error("失败");
})
.then(() => {
console.log(1); // 不执行
})
.then(() => {
console.log(2); // 不执行
})
.catch((error) => {
console.log("catch error", error);
})
.then(() => {
console.log(3); // 3
});

new Promise then then rejectCallback then

new Promise((resolve) => {
throw new Error("失败");
})
.then(() => {
console.log(1); // 不执行
})
.then(
() => {
console.log(2); // 不执行
},
(error) => {
console.log("rejectCallback", error);
}
)
.then(() => {
console.log(3); // 3
});

五、错误链式


Promise 在失败的回调处理完成后, 返回的还是 Promise , 因此可以在失败的回调后继续 .then 或者 .catch

参考资料


Promise的异常穿透和中断Promise的链式请求