微信小程序捕获async/await函数异常实践
背景
我们的小程序项目的构建是与web项目保持一致的,完全使用webpack的生态来构建,没有使用小程序自带的构建功能,那么就需要我们配置代码转换的babel插件如Promise
、Proxy
等;另外,项目中涉及到异步的功能我们统一使用async/await
来处理。我们知道,小程序的onError
生命周期只能捕获同步错误,而完全不采用小程序自带构建工具的情况下,开发模式下遇到的问题:
小程序异步代码中的异常onError无法捕获,开发者工具控制台也没有抛出异常信息
这样在开发过程中页面展示异常,但是无任何异常信息输出,只有代码单步调试时走到异常之处才能发现异常发生的地方,这对开发者很不友好。下面就来说说项目在完全用webpack构建情况下如何在小程序项目中捕获异步代码方面的实践。
几个需要知道的知识点
首先,在切入正文之前介绍几个知识点:
-
小程序
onError
只能捕获同步代码错误,不能捕获异步代码错误。具体原因是因为小程序在内部实现时会对逻辑层的js方法进行
try-catch
封装,对于其中的异步代码异常则不能捕获。 -
try-catch
不能捕获异步异常,但是可以捕获async/await
函数异常。如下面代码的异常try-catch可以捕获:
function asyncFn() { try { await exectionFn() } catch(err) { // exectionFn函数发生的异常可以及时被catch住 console.error(err) } }
-
小程序项目代码中无法访问
window
对象,并不意味着其脱离web渲染。这一点对自定义的babel转换配置来说尤其需要注意,小程序无法访问window对象,即使通过
Function('return this')()
来访问全局作用域也不起作用,因为小程序重写了Function
,如下图源码;具体可以查看从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计这篇文章。
那么,就不能通过window访问该对象上的api,例如window.Promise
。这对根据window是否定义过指定api来判断是否对其转换的babel插件来说意味着,不管怎样都会对
用到的es6新的api进行转换,即使浏览器已经内置了该api的实现。例如
babel-runtime
在转换Promise时就采用polyfill的实现机制,而不是内置实现机制,带来的问题是:Promise的polyfill实现,代码产生的异常在不用Promise.catch或者
unhandledrejection
事件进行捕获的情况下也不会向上抛异常(小程序开发者工具控制台无法得到错误信息),而内置的原生实现则会向上抛这也是为什么采用自定义babel代码转换配置时,控制台无法捕获到异步代码异常信息的原因。
顺便说一下,有小程序经验的同学可能会问,用小程序自带的es6转es5代码转换构建时,异步代码中的异常是可以在小程序开发者工具控制台捕获到的啊;这是因为小程序自带的源码转换只对es6的语法进行转换,而没有对像Promise这样的api进行转换,所以其使用的是原生的Promise实现。
-
babel在转换async/await异步时会有两层
try-catch
封装babel是如何转换async/await的可以看看这篇文章 。下面简单看一下async/await的代码转换的两层try-catch封装。
例如如下代码:
function test() { console.log('hello async') }
转换后的代码如下图:
其中,