try/catch
基本上是你们最常和async/await
一块儿使用的,基本上咱们会用它去包围大部分的异步方法。await
关键字后面的promise
一旦reject
了,就会抛出一个异常错误。javascript
run();
async function run() {
try {
await Promise.ject(new Error('Oops!'));
} catch (err) {
console.error(error.message);
}
}
复制代码
try/catch
一样也能够处理同步的错误,好比下面 :html
async function run() {
const v = null;
try {
await Promise.resolve('foo');
v.thisWillThrow;
} catch (error) {
// 会出现"TypeError: Cannot read property 'thisWillThrow' of null"
console.error(error.message);
}
}
复制代码
return
问题java
好像咱们只要无脑把逻辑都放到try/catch
里面就万事大吉了吗?不太准确,下面的代码却会致使unhandled promise rejection
。这个return
关键字直接返回就错误却不会被捕获:node
async function run() {
try {
// 直接返回Promise,而不是用await关键字
return Promise.reject(new Error('Oops!'));
} catch (error) {
console.error(error.message);
}
}
复制代码
这里的解决方式是使用return await
来解决:golang
async function run() {
try {
return await Promise.reject(new Error('Oops!'));
} catch (error) {
console.error(error.message);
}
}
复制代码
回调问题api
另一个问题是try catch
捕获不了回调函数。try catch 仅仅在单一执行环境中奏效。这里是在回调中加入try catch 来捕获错误:跨域
setTimeout(funciton() {
try {
fn()
} catch (e) {
// handle error
}
})
复制代码
这是奏效的,不过try catch
会在各个地方都出现。而V8引擎是不鼓励try catch
在函数中的使用的。 以前试过把try catch
移到顶层来捕获调用栈的错误,但这个处理对异步代码不会奏效。数组
golang style即便用.then()
的方法来将一个promise
转换为另外一个处理完错误的reject promise
。可使用相似if(err)
来进行检查:promise
async function throwAnError() {
throw new Error('Opps!');
}
async function runAwait() {
let err = await throwAnError();
if (err){
console.error(err.message);
}
}
复制代码
这么写会直接抛出异常,由于这个方法抛出了异常,可是该方法自己没有用try/catch
捕获。不少时候,咱们在使用第三方库的时候可能会出现这种状况。浏览器
then()解决方法
async function runAwait() {
let err = await throwAnError().then(() => null, err => err);
if (err){
console.error(err.message);
}
}
复制代码
then()
的方式,就会等待promise
状态resolve
或reject
后而后执行相应的回调,而后判断err
对象并处理,因此其实它至关于被捕获了。
同时返回错误和值
async function run() {
let [err, res] = await throwAnError().then(v => [null, v], err => [err, null]);
if (err){
console.error(err.message);
}
console.log(res)
}
复制代码
结果:
这么作能够经过解构返回一个数组,包含告终果和error
对象。固然若是是reject
就会返回null
和error
对象;而若是resolved
返回数组的第一个error
对象就为null
,第二个就是结果。
优缺点
catch
。error
对象。run
方法中的同步错误。 因此这种方式须要谨慎使用。上面两种模式均可以处理异步错误,可是对于错误处理,最好的状况是在异步逻辑的最后加上catch
,这样能够保证全部错误都被捕获到。其实这也是一个原则,即统一处理错误,而不是单独去判断并处理每一个错误。
async function run() {
return Promise.reject(new Error('Oops!'));
}
run().catch(function handleError(err) {
console.error(err.message);
}).catch( err => {
process.nextTick(() => { throw errl});
})
复制代码
使用catch
捕获错误,若是handleError
自己也有错误,就须要再catch
一遍,可是为了不回调地狱
,若是该方法发生了错误就终止该进程。
优缺点
catch
的话,无论异步方法自己是否捕获错误,它都会去捕获异步错误。try/catch
没法避免catch
自己抛出异常,而若是它抛出了那除了嵌套多一层try/catch
外,最好的作法就是加catch
来让代码更简洁。浏览器全局处理基本上就是依靠事件,由于浏览器是事件驱动的。一旦抛出错误,解释器在执行环境上下文中中止执行并展开,此时会有一个onerror
全局事件抛出:
window.addEventListener('error', function (e) {
var error = e.error;
console.log(error);
})
复制代码
全局错误处理器会捕获任何在执行环境中发生的错误,即使是不一样的对象发生的错误事件,或者是各类类型的错误。这是全局集中处理错误的一种常见方式。
调用栈
调用栈在定位问题的时候十分重要,咱们可使用调用栈在处理器中处理特定的错误。
window.addEventListener('error', function (e) {
var stack = e.error.stack;
var message = e.error.toString();
if (stack) {
message += '\n' + stack;
}
var xhr = new XMLHttpRequest();
xhr.open('POST', '/log', true);
// Fire an Ajax request with error details
xhr.send(message);
});
复制代码
经过日志能够看到,具体什么状况触发了什么错误。在调试时调用堆栈也会很是有用。你 能够分析log,看到什么条件下触发了错误。
注意:
若是跨域脚本是不会看到错误的。 在JS中,错误信息仅仅是容许在同一个域中。
我的想法
更多的时候,代码抛出了异常,咱们更关注的是在运行时,某个变量的值是什么,是否这个变量的值致使了错误,因此打印出调用时的跟多的信息更重要。
Node.js自己的异常处理要复杂得多,由于涉及到了进程或线程抛出异常的问题。
基于Koa的全局错误处理
nodejs是error-first
的异步处理机制,此处底层会调用net
模块的listen
方法并在错误发生时执行回调。
app.listen(app.config.listenPort, (err) => {
if (err) throw err
app.logger.info(`> Ready on http://localhost:${app.config.listenPort}`)
})
复制代码
路由错误处理
对于每一个路由,它可能也会有不一样的错误处理逻辑,这时路由进来的请求就须要根据状况返回不一样的异常码和信息。
router.get('/loginAuth', async (ctx, next) => {
try {
const code = query.code
const res = await requestToken(code)
if (res.data.code !== 0) {
ctx.app.logger.error(`request token error.Code is ${res.data.code} || response is: ${JSON.stringify(res.data.data)} || msg: ${res.data.message}`)
ctx.body = {
code: 10000,
message: `request token by code error`
}
} else {
ctx.body = res.data
}
} catch (err) {
ctx.app.logger.error(`request api has exception ${ctx.request.url} || ${err.code} || ${err.message} || ${err.stack}`)
ctx.body = {
code: 500,
message: `Error response`
}
}
})
复制代码
try/catch
没有问题。catch
来保证它们会被捕获到。window
对象上,它会捕获到异步错误,符合了DRY
和SOLID
原则。一个全局的错误处理器能够帮你保持异步代码整洁。