更优的调试方式
如何让我们在调试代码时,既看到具体的报错信息,又能不影响当前的执行环境, 使用 try-catch
的方式会帮我们静默捕获
程序出错是不能走正常的逻辑,所以我们仅是记录错误信息,hasError 等并且去走出错的逻辑,
React 的 ErrorBandage
渲染错误的备用 UI,这里仅是能看到报错的具体信息,但是又不影响出错的逻辑。
// 模拟出效果
function toggleError() {throw Error()
}
console.log('script start')
toggleError()
console.log('script end')
// 实际执行效果
script start
Uncaught Error
// 最终期望的执行结果
script start
Uncaught Error
script end
事件回调系统
事件回调系统拥有自己的上下文,即便在自己的事件回调系统中触发了错误也不会影响到整个事件的处理系统
// 使用事件回调系统
document.addEventListener("DOMContentLoaded", function() {console.log("Init: 1");
DOES_NOT_EXIST++; // error
}, false);
document.addEventListener("DOMContentLoaded", function() {console.log("Init: 2");
}, false);
// 此时 console 中会显示如下内容
Init: 1
Uncaught ReferenceError: DOES_NOT_EXIST is not defined
Init: 2
// jQuery 中使用回调系统实现
$(document).ready(function() {console.log("Init: 1");
DOES_NOT_EXIST++; // error
});
$(document).ready(function() {console.log("Init: 2");
})
// 此时 console 中会显示如下内容
Init: 1
Uncaught ReferenceError: DOES_NOT_EXIST is not defined
可以看到使用了事件处理函数会使得某个回调事件中发生了错误不会破坏到整个系统
不同环境下处理错误的不同
生产环境使用的try-catch
此时发生的错误会被我们捕获,并且继续进行后续的执行,(比如 React 中 ErrorBandage 可渲染备用的错误 UI)
function toggleError() {throw Error()
}
try {toggleError()
} catch (e) {// 处理错误信息}
开发环境中
当然使用 try-catch
是没有问题的,但是既然我们正在开发,就应该得知具体的报错情况,并且处理掉,但是我们一旦使用 try-catch
就会帮我们静默的处理,并不会暴露错误,chrome 中有调试工具 pause on caught exception
只有出错就暂停到此处, 所以我们使用一种曲线救国的方式,使用事件回调的方式去运行,报错并不会影响到外部环境。
模拟 React 中的invokeGuardedCallback
,即将回调放到一个自定义事件函数中,立即触发,有错误不会影响外部
let hasError = true
let error = null
function invokeGuardedCallback(callback) {
// 全局 error 事件,有错误记录 error 信息
function handleWindowError(event) {error = event.error}
window.addEventListener('error', handleWindowError);
// 自定义事件处理回调
const eventType = 'fakeErrEventType'
const evt = document.createEvent('Event')
evt.initEvent(eventType, false, false)
const fakeNode = document.createElement('fake-err')
// 处理回调
function callCallback() {fakeNode.removeEventListener(eventType, callCallback, false)
callback.call(null)
// 默认有错误,如果正常执行 callback,则认为没有错误发生
hasError = false
}
// 绑定自定义事件并触发
fakeNode.addEventListener(eventType, callCallback, false)
fakeNode.dispatchEvent(evt)
}
不同方法处理实例
// 目标回调函数 toggleError
function toggleError() {throw Error()
}
不进行错误处理
console.log('script start')
toggleError()
console.log('script end')
// 执行结果
script start
Uncaught Error
使用 try-catch
处理
console.log('script start')
try {toggleError()
} catch (err) {// err 错误相关信息}
console.log('script end')
// 执行结果,并不能看出错误详情位置,可能会忽略发生了错误,因为被处理了
script start
script end
使用invokeGuardedCallback
console.log('script start')
invokeGuardedCallback(toggleError)
if (hasError) {// error 是全局捕获的出错信息}
console.log('script end')
// 执行结果,并不能看出错误详情位置,可能会忽略发生了错误,因为被处理了
script start
Uncaught Error
script end
参考资料
https://www.cnblogs.com/fangzhaolee/p/3719384.html
https://github.com/facebook/react/blob/22f7663f14f12ebd6174292931e94d2b352cf666/packages/shared/invokeGuardedCallbackImpl.js#L61