# try/catch
try...catch语句标记要尝试的语句块,并指定一个同步函数出现异常时抛出的响应。
看看使用方法:
try {
throw new Error('The error')
// .. 下面代码不会执行
console.log(22)
} catch (err) {
console.log(err) // 'The error'
}
使用一些不存在的方法,或操作方式错误时,就会进入catch
,也是避免某个方法出错,而造成页面奔溃
try {
JSON(2)
} catch (err) {
console.log(err) // JSON is not a functio
}
# 无法捕获
try {
setTimeout(() => {
throw new Error('async error') // Error
}, 1000)
} catch(e) {
console.log(e, 'err')
}
这段代码中,setTimeout
的回调函数抛出一个错误,并不会在 catch
中捕获,会导致程序直接报错崩掉。
所以说在 js 中 try/catch
并不是说写上一个就可以高枕无忧了。难道每个函数都要写吗,那什么情况下 try/catch
无法捕获 error
呢?
- 宏任务的回调函数中的错误无法捕获
// 异步任务
const task = () => {
setTimeout(() => {
throw new Error('async error') // Error
}, 1000)
}
// 主任务
function main() {
try {
task()
} catch(e) {
console.log(e, 'err')
}
}
main()
这种情况下是无法进入 catch
的,这跟浏览器的执行机制有关。
异步任务由 eventloop
加入任务队列,并取出入栈(js 主进程)执行,而当 task
取出执行的时候, main
的栈已经退出了,也就是上下文环境已经改变,所以 main
无法捕获 task
的错误。
- 微任务(promise)的回调
// 返回一个 promise 对象
const promiseFetch = () => {
return new Promise((reslove) => {
reslove();
})
}
function main() {
try {
promiseFetch().then(() => {
throw new Error('err') // Error
})
} catch(e) {
console.log(e, 'err');
}
}
main()
promise
的任务,也就是 then
里面的回调函数,抛出错误同样也无法 catch
。
因为微任务队列是在两个 task
之前清空的,所以 then
入栈的时候,main
函数也已经出栈了。
这是事件循环机制的问题,同步任务和异步任务。
# Promise 的异常捕获
function main1() {
try {
new Promise(() => {
throw new Error('promise1 error') // Error
})
} catch(e) {
console.log(e.message);
}
}
function main2() {
try {
Promise.reject('promise2 error') // Error
} catch(e) {
console.log(e.message);
}
}
以上两个 try catch
都不能捕获到 error
,因为 promise
内部的错误不会冒泡出来,而是被 promise
吃掉了,只有通过 promise.catch
才可以捕获,所以用 Promise
一定要写 catch
。
然后我们再来看一下使用 promise.catch 的两段代码:
// reject
new Promise((reslove, reject) => {
reject()
}).catch((e) => {
console.log('error') // error
})
// throw new Error
new Promise((reslove, reject) => {
throw new Error('error')
}).catch((e) => {
console.log('error') // error
})
promise 内部的无论是 reject
或者 throw new Error
,都可以通过 catch
回调捕获。
这里要跟我们最开始微任务的栗子区分,promise
的微任务指的是 then
的回调,而此处是 Promise
构造函数传入的第一个参数,new Promise
是同步执行的。
那 then 之后的错误如何捕获呢。
Promise.resolve(true).then(() => {
try {
throw new Error('then');
} catch(e) {
console.log('err') // err
return e
}
}).then(e => {
console.log(e) // then
})
只能是在回调函数内部 catch
错误,并把错误信息返回,error
会传递到下一个 then
的回调。
# async/await 的异常捕获
依旧分两种情况,是Promise
对象和非Promise
对象。
第一种情况:
async function main () {
try {
const res = await new Promise((resolve, reject) => {
reject('fetch failure...')
})
console.log(res, 'res')
} catch(e) {
console.log(e, 'e.message') // fetch failure... e.message
}
}
main()
async function main () {
try {
const res = await Promise.reject(2)
console.log(res, 'res')
} catch(e) {
console.log(e, 'e.message') // 2 e.message
}
}
main()
Promise带catch
回调则不会进入下方的catch
捕获,如果Promise的catch
回调中出错,那么还是会走下方的catch
捕获
async function main () {
try {
const res = await new Promise((resolve, reject) => {
reject('fetch failure...')
}).then(res => {
console.log(3, res)
}).catch(res => {
console.log(2, res) // 2 "fetch failure..."
})
console.log(res, 'res') // undefined "res"
} catch(e) {
console.log(e, 'e.message')
}
}
main()
async function main () {
try {
const res = await new Promise((resolve, reject) => {
resolve('fetch failure...')
}).then(res => {
JSON(3) // 链式catch捕获then错误
}).catch(res => {
JSON(3) // 下方catch捕获当前catch错误
})
console.log(res, 'res')
} catch(e) {
console.log(e, 'e.message') // JSON is not a function "e.message"
}
}
main()
规则与前面的Promise基本一致的。
不带Promise:
async function main () {
try {
const res = await new Promise((resolve, reject) => {
resolve('fetch failure...')
}).then(res => {
console.log(3)
}).catch(res => {
JSON(3)
})
JSON(2)
console.log(res, 'res')
} catch(e) {
console.log(e, 'e.message') // JSON is not a function 'e.message'
}
}
main()
await
只是Generator
的语法糖,所以同样会被catch
捕获到,区别在于await
语句是否为Promise,其规则也与Promise一致