写点什么

我发现了 Chrome 的一个 bug

用户头像
码生笔谈
关注
发布于: 2 小时前
我发现了Chrome的一个bug

故事的开始

最近在项目中遇到一个问题,业务逻辑就不在这里介绍了,在排查过程中发现项目里有类似这样一段代码:


fetch(url)  .then((res) => res.text())  .then((text) => {    const data = JSON.parse(text)  })
复制代码


上面没有对最后一个then进行catch操作,我们怀疑这里出了问题,因为text可能无法解析为合法的JSON,然而事实上我们的项目里是有全局的错误捕获的:


window.addEventListener('unhandledrejection', (e) => {  sendLog(e)})
复制代码


这就奇怪了,华容逢关羽吗?,难道unhandledrejection事件不仅没有捕获到JSON.parse的报错还让它走脱了吗?然后我们做了下面这样代码的尝试:


window.addEventListener('unhandledrejection', event => {  console.log('unhandledrejection:', event);})
Promise.resolve("{") .then((data) => { JSON.parse(data) })
复制代码


在控制台中出现下下面的报错信息:


Uncaught (in promise) SyntaxError: Unexpected end of JSON input  at JSON.parse (<anonymous>)  ...
复制代码


可以看到这个语法错误是因为JSON.parse无法成功解析的缘故,确实是没有被unhandledrejection捕获,难道是这个事件出了问题吗?索性我们直接抛出错误,看这个事件能不能正常工作:


Promise.resolve()  .then(() => {    throw 123  })
复制代码


控制台出现了下面的信息:


unhandledrejection: 123
复制代码


说明unhandledrejection事件是没问题的,那为什么JSON.parse的错误不能捕获呢?难道是无法捕获语法错误吗?那我们换一种eval的方式解析JSON,如果无法捕获语法错误,那么eval报的语法错误肯定也无法捕获:


Promise.resolve("{")  .then((data) => {    eval(data)  })
复制代码


我们再看控制台的输出:


unhandledrejection: SyntaxError: Unexpected end of input
复制代码


蒙圈了吧,这个语法错误居然被捕获到了,我们又想,难道说JSON.parse的报错很特殊吗?我们又做了下面的尝试:


Promise.resolve("{")  .then((data) => {    try {      JSON.parse(data)    } catch (e) {      throw e    }  })
复制代码


上面尝试将报错通过try...catch...捕获到,然后再重新抛出,然后控制台输出:


Uncaught (in promise) SyntaxError: Unexpected end of JSON input  at JSON.parse (<anonymous>)
复制代码


上面说明错误仍然没有捕获到,难道是错误信息有问题?那我们把错误包装一下呢:


Promise.resolve("{")  .then((data) => {    try {      JSON.parse(data)    } catch (e) {      const wraper = new SyntaxError(e.messgae)      wraper.stack = e.stack      throw wraper    }  })
复制代码


上面创建了一个SyntaxError的实例wraper,然后把try...catch...捕获到的错误信息放到wraper上,抛出wraper,控制台输出如下:


unhandledrejection: SyntaxError: Unexpected end of input
复制代码


看起来错误被捕获到了,我们开始怀疑人生,于是在stackoverflow提了这个疑问:stackoverflow问题地址,内容基本和上文一致,感兴趣的同学可以去看看,另外,对于上面问题在FirefoxSafari中并未出现。

有意思的事

stackoverflow的评论中,一位叫Kaiido的开发者说这可能是Chromebug,建议我去chromium bug报告网址报告一下,没想到我搜索到了类似的报告,报告人说他发现window.onerrorunhandledrejection这两个事件都无法捕获到JSON.parse的错误:


window.onerror = async (...args) => {  console.log("onerror")}
window.addEventListener('unhandledrejection', async function (event) { console.log("unhandledrejection")})
var elem = document.getElementById('button');elem.addEventListener('click', () => { JSON.parse(undefined); })
复制代码


上面报告人监听的两个事件,说最终都没有被触发,大家也可以试一下,这个例子由于JSON.parse不在Promise中,所以window.onerror是能够捕获的,也就是说报告人的提问不是很恰当,下面维护者的回复就比较搞笑了,这位维护者打开了与报告人相同版本(chrome88)的浏览器和黑暗模式,然后打开chrome空白页,将上面代码粘贴到控制台执行,然后出现下面报错:


Uncaught TypeError: Cannot read property 'addEventListener' of null  at <anonymous>:11:6
复制代码


然后维护者让报告人指出问题的所在,大家应该都看得出来,这个报错是因为页面上没有button元素导致的,与报告人所说的错误没有任何关系,所以我怀疑这个维护者是来搞笑的,时隔几乎一个月,今天上午报告人使用了我在stackoverflow提出的问题作为例子来向维护者说明问题,目前维护者还未回复,大家可以持续关注下

最后

在上面问题还没解决的情况下,我们最好还是对现有代码做一下审查,对于使用Promise的地方在then后面一定要加catch方法,对于直接使用JSON.parse的位置,根据对参数的了解情况酌情添加try...catch...


对于前端错误监控平台来说,这也是一个棘手的问题,希望未来能从中看到更好的解决方案。


封面图:愚蠢的美人鱼 by 铁柱呆又呆


关注「码生笔谈」公众号,阅读更多有趣文章

发布于: 2 小时前阅读数: 7
用户头像

码生笔谈

关注

欢迎关注「码生笔谈」公众号 2018.09.09 加入

前端、Rust干货分享,一起成为更好的Javascripter & Rustacean

评论

发布
暂无评论
我发现了Chrome的一个bug