本文以Node.js环境为例,Node.js使用的JavaScript引擎是V8,所以理论上Chrome也可以适用,我会不要将它用于其他浏览器。清除。现状最近在写Rize(Welcometostar)的时候,一直在担心错误的stacktraces。为什么?这要从Rize的架构说起。由于puppeteer的大部分操作和API都是异步的,因此编写异步代码的一个好方法是使用ES2017的async/await语法。但是我们都知道async/await实际上返回的是一个Promise(即使你没有显式返回任何东西,它也会是Promise)。显然,这样是达不到我想要的API链式调用的效果的。总不能在Promise实例上操作原型,然后把自己的API搬过去吧?所以我用一个队列来保存用户想要做的动作。也就是说,用户调用Rize的API后,这些操作不会(也不能)立即执行,而是放在队列中等待合适的时机(比如浏览器已经启动或者之前的操作已完成)执行前。由于函数是发送到队列中的,所以push的参数可以放心使用async/await。但是,一旦这些操作出现错误,错误的定位就变得非常麻烦。下图是直接用Node.js运行一个脚本的结果:下图是在Jest中执行一段代码的结果:原因是,首先队列中的函数是一个async函数,这带来了麻烦。其次,这些函数不会在API中立即调用,而是由专门的队列处理代码调用。当错误发生时,V8只能追踪到队列处理代码的那部分。这给用户带来了麻烦。报错了,只能一点点看报错信息来尝试定位问题。为此,我阅读了Node.js的官方文档,阅读了Errors部分,但似乎并没有什么收获。后来找了TJHolowaychuk写的librarycallsite看看能不能用。从文档来看,这个库不适合我的需要。但是我看了callsite的源码,源码很短,不到十行。我在源代码中找到了一些信息。callsite使用V8的StackTraceAPI获取函数调用的一些信息,比如文件名、行号等。callsite是如何获取这些数据的?很简单,就一句话:varerr=newError()是的,它只是一个新的Error实例,并不是为了抛出这个错误。相对于我们平时的代码,通常我们在抛出错误的时候,可以得到一些错误的堆栈信息。但其实不需要抛出,只需要新建一个Error实例也可以让V8记录当前的调用栈信息。解决既然我们已经发现了这个事实,那么我们就可以创建一个新的Error实例,我们需要在其中记录调用堆栈。(不要扔,不然你后面的代码执行不了)这个时候,当前的栈信息已经记录下来了,那我们怎么利用这些信息呢?如果用户的代码执行得很好,那没关系。关键是什么时候出了问题。在这里我想提一下的是,我的队列处理代码有一个try...catch块,它看起来像这样:.}你可能会疑惑为什么要抛出捕获到的异常,因为我想要的是后面的finally块,但同时又希望异常继续抛出。在这里,我们将对catch块做一些工作。当然,这个try...catch块可以得到新创建的Error实例,所以我这里省略了那部分代码。为了描述方便,我将新的Error实例命名为trace,即假设consttrace=newError()。显然不适合把trace的所有栈信息都拿过来,因为它有一些我们不需要的栈信息(这部分信息位于API调用之上)。每个Error实例都有一个堆栈属性,它是一个多行字符串。我们先把它的每一行分开保存在一个数组中:conststack=trace.stack!.split('\n')注意stack的第一行不是栈信息,而是错误信息,不能被删除。所以:stack.splice(1,2)我这里有两行信息没用,所以把这两行删掉,其实根据自己的需要修改第二个参数。现在可以将trace的堆栈信息替换成实际的错误堆栈信息:error.stack=stack.join('\n')结果,现在可以得到一个友好的错误堆栈信息:配合Jest更好定位问题优点:最后,我想推广一下我正在写的库Rize(它可以让你简单优雅地使用puppeteer),这篇文章中有提到。欢迎来到GitHub和star。原博客在这里