在同步代码和异步代码中错误的捕获机制是不一样的,如果忽视这点只是简单的加 try…catch可能会忽视某些错误的捕获。本篇代码已经抽象为极简模式,可以多try try看看效果,既能理解错误捕获还可以理解任务执行机制
可以先看看对 JavaScript 中的 try-catch 能否捕获异步错误的探究 前置了解
同步错误捕获
先看看下面的这段代码
function testA(obj) {
const a = obj.a;
return a;
}
function testB(input){
try{
const res = testA(input)
console.log('BBB', res);
} catch(e) {
console.error('同步错误捕获')
}
}
testB(null)
testB({})
最后控制台打印输出
分析:当执行testB(null)代码执行testA函数时会报错,从而被catch住;而执行testB({})的时候则正常执行打印
异步错误捕获
当异步任务重嵌套的同步任务能捕获到吗?
先看看下面的这段代码
function testA(obj) {
const a = obj.a;
return a;
}
function testB(input){
let res;
new Promise(() => {
res = testA(input);
}).then(() => {
console.log('BBB', res);
}).catch((e) => {
console.error('捕获到了整个promise里面的错误')
})
console.log('CCC', res);
}
testB(null)
testB({})
可以看出是先执行同步任务testB(null) 打印同步任务CCC Promise放入微任务队列
执行同步任务testB({}),Promise 没有 resolve 或 reject,因此 then 和 catch 中的代码不执行。打印同步任务CCC
同步任务执行完,查看任务队列发现Promise取出微任务Promise执行
接着执行Promise微任务 这时候执行微任务的时候遇到了里面的同步任务testA,这样即便同步任务testA里面有错误,也会被Promise那层的catch住,因此打印了“捕获到了整个promise里面的错误”
或许上面代码让你有点乱,可以使用async 和 await 的语法糖构建成同步形式就好看啦
function testA(obj) {
const a = obj.a;
return a;
}
async function testB(input){
let res;
await new Promise(() => {
res = testA(input);
console.log('AAA', res);
}).then(() => {
console.log('BBB', res);
}).catch((e) => {
console.error('捕获到了整个promise里面的错误')
})
console.log('CCC', res);
}
testB(null)
// testB({})
执行同步任务testB(null)的 await同步语法糖等待Promise执行
执行testA,因此catch输出"捕获到了整个promise里面的错误",然后打印CCC,至此整个Promise成fulfilled。
如果这里没有await,那就先打印CCC啦,再走Promise 然后 catch 住错误
而执行testB({}) 中执行完res = testA(input); 我没有进行resolve 因此一直处于pending状态,await 会暂停函数执行,等待后面的 Promise 解决(resolve 或 reject)。因此也不会打印CCC了,到不了那里了
倘若await则全看成同步代码输出就好,testB会结束
跑题了,不是来看同步异步的,我们是看异常捕获的!!!感兴趣的小伙伴自行学习
同步异步错误同时捕获
上面已经可以看到,在异步里面的同步错误,是可以被最外层的异步任务的catch捕获到的,所以有时候我们不需要写很多try catch
那同步任务和异步任务同时都有的时候怎么办呢?例如下面的一段代码
function testA(obj) {
const a = obj.a;
return a;
}
function testC(obj) {
const b = obj.b;
return b;
}
async function testB(input1, input2){
let res;
res = testC(input2);
await new Promise(() => {
res = testA(input1);
console.log('AAA', res);
}).then(() => {
console.log('BBB', res);
}).catch((e) => {
console.error('捕获到了整个promise里面的错误')
})
console.log('CCC', res);
}
testB(null, null)
这样就丸辣,因为同步任务并没有catch住(这里仔细看看有几个同步代码~)
因此,要搭配try catch 捕获同步异常
function testA(obj) {
const a = obj.a;
return a;
}
function testC(obj) {
const b = obj.b;
return b;
}
async function testB(input1, input2){
let res;
try{
res = testC(input2);
await new Promise(() => {
res = testA(input1);
console.log('AAA', res);
}).then(() => {
console.log('BBB', res);
}).catch((e) => {
console.error('捕获到了整个promise里面的错误')
})
console.log('CCC', res);
} catch (e) {
console.error('捕获同步错误')
}
}
testB(null, null)
// testB(null, {})
// testB({}, null)
// testB({})
可以看出,最外层的同步错误已经catch了,内层的错误不会再报了而直接fulfilled
有童鞋说这样也太冗余了,一会儿try catch 一会儿catch,那就最外层加一个就好了嘛,是不是一起就捕获了
function testA(obj) {
const a = obj.a;
return a;
}
function testC(obj) {
const b = obj.b;
return b;
}
async function testB(input1, input2){
let res;
try{
res = testC(input2);
await new Promise(() => {
res = testA(input1);
console.log('AAA', res);
}).then(() => {
console.log('BBB', res);
})
// .catch((e) => {
// console.error('捕获到了整个promise里面的错误')
// })
console.log('CCC', res);
} catch (e) {
console.error('捕获同步错误')
}
}
testB(null, null)
看起来没问题嘛,还是,当只有里面出问题的时候还是捕获了。你再仔细看看呢,由于加了await语法糖,实际上你这个是同步代码!!!所以同步代码均被外面的try catch捕获了。所以上面让看看有几个同步代码~
真实的场景是这样的同步夹杂异步,也就是去掉await async语法糖
看这样是不是没有捕获住??
如果外面同步代码没问题,而内部代码出问题则会被内部的捕获,所以是没问题的,到谁那儿捕获谁嘛,虽然使用了await语法糖,但是内层对异步处理的catch仍然捕获到了!!!主打一个就近原则
这时你把await去掉成异步也没问题的