使用 Chrome DevTools 调试异步 JavaScript 程序
使 JavaScript 独一无二的一个强大功能是它能够通过回调函数异步工作。 分配异步回调使您可以编写事件驱动的代码,但它也使跟踪错误成为一种令人毛骨悚然的体验,因为 JavaScript 不是以线性方式执行的。
幸运的是,现在在 Chrome DevTools 中,您可以查看异步 JavaScript 回调的 完整 调用堆栈!
异步调用堆栈的快速预告概述,我们很快就会分解这个演示的流程。
在 DevTools 中启用异步调用堆栈功能后,您将能够深入了解 Web 应用程序在不同时间点的状态。 遍历某些事件侦听器的完整堆栈跟踪, setInterval
, setTimeout
, XMLHttpRequest
, promise, requestAnimationFrame
, MutationObservers
, 和更多。
当您遍历堆栈跟踪时,您还可以分析任何变量在该特定运行时执行点的值。 它就像您的手表表情的时间机器!
让我们启用此功能并看一下其中的一些场景。
在 Chrome 中启用异步调试
通过在 Chrome 中启用此新功能来试用它。 转到 Chrome Canary DevTools 的 Sources 面板的 调用堆栈 在右侧 切换复选框以打开或关闭异步调试。 (虽然一旦打开,您可能永远不想关闭它。)
捕获延迟的计时器事件和 XHR 响应
您之前可能在 Gmail 中看到过:
如果发送请求时出现问题(服务器出现问题或客户端存在网络连接问题),Gmail 会在短暂超时后自动尝试重新发送邮件。
为了了解异步调用堆栈如何帮助我们分析延迟的计时器事件和 XHR 响应,我使用 模拟 Gmail 示例 。 完整的 JavaScript 代码可以在上面的链接中找到,但流程如下:
在上图中,以蓝色突出显示的方法是这个新的 DevTool 功能最有利的地方,因为这些方法是异步工作的。
通过只查看以前版本的 DevTools 中的调用堆栈面板, postOnFail()
会给你很少的关于在哪里的信息 postOnFail()
被调用。 但是看看开启异步堆栈时的区别:
前
的调用堆栈面板 未 启用异步
在这里你可以看到 postOnFail()
是从 AJAX 回调发起的,但没有更多信息。
后
调用堆栈面板 的 启用异步
在这里您可以看到 XHR 是从 submitHandler()
。
打开异步调用堆栈后,您可以查看整个调用堆栈以轻松查看请求是否从 submitHandler()
(在单击提交按钮后发生)或来自 retrySubmit()
(这发生在之后 setTimeout()
延迟):
提交处理程序()
重试提交()
异步观察表达式
当您遍历完整的调用堆栈时,您监视的表达式也将更新以反映它当时所处的状态!
从过去的范围评估代码
除了简单地 DevTools JavaScript 控制台面板中与以前作用域中的代码进行交互。
想象一下,你是 Dr. Who,你需要一些帮助来比较你进入 Tardis 之前和“现在”的时钟。 从 DevTools 控制台,您可以轻松地评估、存储和计算来自不同执行点的值。
将 JavaScript 控制台与异步调用堆栈结合使用来调试您的代码。 上面的演示可以在 这里 。
留在 DevTools 中操作表达式将节省您切换回源代码、进行编辑和刷新浏览器的时间。
解开链式承诺解决方案
如果您认为之前的模拟 Gmail 流程在不启用异步调用堆栈功能的情况下很难解开,您能想象使用更复杂的异步流程(如链式 Promise)会有多困难吗? 让我们重温一下 Jake Archibald 关于 JavaScript Promises 。
中遍历调用堆栈的小动画 async-best-example.html 示例
前
的调用堆栈面板 未 启用异,请注意,在尝试调试 Promise 时,调用堆栈面板的信息非常短缺。
后
调用堆栈面板 的 启用异步,这样的 Promise 很多回调。
深入了解您的网络动画
让我们更深入地了解 HTML5Rocks 档案。 还记得 Paul Lewis 用 requestAnimationFrame 实现的更精简、更简洁、更快的动画 吗?
打开 requestAnimationFrame demo ,在开头添加断点 update()
post.html 的方法(大约第 874 行)。 借助异步调用堆栈,我们可以更深入地了解 requestAnimationFrame
,包括一直走到发起滚动事件回调的能力。
前
的调用堆栈面板 未 启用异步 后
启用异步
使用 MutationObserver 时跟踪 DOM 更新
MutationObserver
允许我们观察 DOM 的变化。 在这个 简单的例子 中,当你点击按钮时,一个新的 DOM 节点被追加到 <div class="rows"></div>
。
在里面添加断点 nodeAdded()
(第 31 行)在 demo.html 中。 启用异步调用堆栈后,您现在可以遍历调用堆栈 addNode()
到初始点击事件。
前
的调用堆栈面板 未 启用异步
启用异步
在异步调用堆栈中调试 JavaScript 的技巧
命名你的函数
如果您倾向于将所有回调分配为匿名函数,则可能希望为它们命名,以便更轻松地查看调用堆栈。例如,采用这样的匿名函数:
window.addEventListener('load', function(){ // 做一点事 });
并给它起一个名字 windowLoaded()
:
window.addEventListener('load', function windowLoaded (){ // 做一点事 });
当 load 事件触发时,它将以其函数名称而不是神秘的匿名函数,显示在 DevTools 堆栈跟踪中。 这使得一目了然地查看堆栈跟踪中发生的事情变得更加容易。
前
后
进一步探索
回顾一下,这些是 DevTools 将在其中显示完整调用堆栈的所有异步回调:
- Timers:走回哪里
setTimeout()
或者setInterval()
被初始化。 - XHRs :走回哪里
xhr.send()
被称为。 - Animation frames:回到调用 requestAnimationFrame 的位置。
- Promises : 回到 promise 被解决的地方。
- Object.observe :回到观察者回调最初绑定的位置。
- MutationObservers :返回到触发突变观察者事件的位置。
- window.postMessage() :遍历进程内消息调用。
- DataTransferItem.getAsString()
- FileSystem API
- IndexedDB
- WebSQL
- 符合条件的 DOM 事件通过
addEventListener()
:回到触发事件的地方。 由于性能原因,并非所有 DOM 事件都符合异步调用堆栈功能。 当前可用事件的示例包括:scroll、hashchange 和 selectionchange。 - 多媒体活动通过
addEventListener()
:回到触发事件的地方。 可用的多媒体事件包括:音频和视频事件(例如 play、pause、ratechange)、WebRTC MediaStreamTrackList 事件(例如 addtrack、removetrack)和MediaSource 事件(例如'sourceopen')。
能够看到你的 JavaScript 回调的完整堆栈跟踪应该会让你头疼。 当多个异步事件相互关联时,或者从异步回调中抛出未捕获的异常时,DevTools 中的此功能将特别有用。
在 Chrome 中尝试一下。 如果您对此新功能有任何反馈,请在 Chrome DevTools 错误跟踪器 或 Chrome DevTools Group 。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论