事件回调系统中处理错误

28次阅读

共计 2665 个字符,预计需要花费 7 分钟才能阅读完成。

更优的调试方式

如何让我们在调试代码时,既看到具体的报错信息,又能不影响当前的执行环境, 使用 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

正文完
 0