边学边译JS工作机制--34. 调试异步代码的4个提示

766 阅读9分钟

本系列其他译文请看[JS工作机制 - 小白1991的专栏 - 掘金 (juejin.cn)] (juejin.cn/column/6988…

本文阅读指数:2 都是基本的调试方式,价值不太高

调试是软件开发的一个重要部分,任意开发者在这个领域都可以深入的提升。在发布环境中遗留下重大bug会给你的应用和公司带来巨大的损失。调试程序包括找bug和修Bug 在调试过程中,你可以单步调试代码,确定异常发生的额地方,以及引起异常的原因。所有的现代浏览器都内置了JS提示器。JS内部也有一些方法和标记,方便我们调试。 在JS中调试同步和异步代码略有不同。因为,在异步中,函数的调用和返回是不按顺序的。如果在规定时间内没有响应,超时也是一个bug.这一章,我们会讨论如何在JS中调试,以及调试异步代码的提示。

为什么调试很重要

调试通过查找bug和不正确代码,来避免应用中大多数的不正确行为。 bug经常让你的应用崩溃。使用适当的调试,你可以从代码中发现问题,避免一些问题。 例如,使用调试你可以确定运行时或编译时错误的原因。一些代码或许不会抛出异常或错误,但是会变现异常。例如,指定一些不正确的变量引发一些意外的行为。

let a = 5;
let b = 10;
let c = 12;
let d = a + b;// Developer was meaning to write let e = a + c;
let e = b + c;

上面代码中,开发之期待17,但是获得了一个22。如果开发者使用console.log() 调试代码,他们可以在控制台跟踪异常。有时候,开发者使用一些工具和程序开创建应用。用正确的调试,开发者可以确定他们的应用整体的表现。

如何调试JS应用

大多是web应用都使用JS,手机应用也逐渐使用JS了。在推送他们发布环境之前,开发者需要调试它们。

有时,开发者需要比写代码更加注意。看一下调试JS应用的调试过程:

定位Bug

调试的第一步是定位bug。工程师可以异常发生时或者只是检查代码来发现bug。

调试要么是主动地,要么是被动的。被动的调试,由开发者来掌控调试步骤。换句话说,就是开发者可以从执行步骤中收集日志和有效数据。

但是,有一些场景需要开发者在调试时跟应用通信。例如,开发者可能需要给应用发送数据,以便调试应用的行为表现。这种场景使用被动调试时无法满足的,因为被动调试只包含执行调试步骤和打印调试程序中的数据。因此,开发者使用主动方式来调试。主动调试可以让开发者跟应用进行通信

分离bug

分离bug的过程是确定程序的哪一块代码引起了异常。在这一步,开发者尝试从应用中分离引起异常的代码部分,这样可以更加彻底的调试。这个步骤一般包含这些方法:

1.正向分析

这个方法,开发者向前单独调试代码,有时放置一些断点。一旦有异常被记录到,开发者就聚焦于这块区域,确定异常的原因。

2. 反向分析

有时候,开发者需要反向跟踪异常,从当前的断点到前面的断点去查找异常的原因。这就是反向分析

确定bug原因

分离bug之后,就是调试分离的部分来确定原因。例如,如果异常围绕一个不正确的输入字段,就会调试所有的输入字段以确定bug原因。

修复Bug

一旦确定了Bug原因,下一步就是修复。如果发现这是个比较深层的问题,关系到应用架构,就需要更加资深的开发者来处理它

测试应用

调试是一个反复的过程,从开发一直到上线。修复Bug之后,开发者要继续测试,看是否有其他问题。有时候,修复bug之后反而生出更多的Bug。

JS中的测试方法

这一节,我们要讨论调试JS的一些方式:

Web 控制台

多数浏览器都带有控制台,方便开发者记录页面有关的信息。控制台的一个优势是,你可以在上面执行JS表达式。 来看一些跟控制台有关的调试方法吧。 假如我们像调试的是这段代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Debugging Test</title>
</head>

<body>
    <p id="demo">Click the button to display greeting.</p>
    <button onclick="myFunction()">Display Greeting</button>
    <script>
        function myFunction() {
            document.getElementById("demo").innerHTML = "Hello World";
        }
    </script>
</body>

</html>
  1. 日志

快速调试,使用console.log() 方法。 在我们的例子中,我们想要确保按钮工作正常,就要确保调用的 id 是正确的。当我们调用的 id 和按钮的 id不一致时,我们可以log一个信息。

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Debugging Test</title>
</head>
<body>
   <p id="demo">Click the button to display greeting.</p>
   <button onclick="myFunction()">Display Greeting</button>
   <script>
       function myFunction() {
       let id = document.getElementById("demo").id;
       let correctId = "demo";
       if (id === correctId) {
           document.getElementById("demo").innerHTML = "Hello World";
       }else console.log ("ID doesn't match, id is", id.id)
   }
   </script> 
</body>
</html>

这里我们不会得到任何日志,因为我们调用的“demo”id和按钮的id是一致的。如果开发者指定按钮id 为“demi”,那就可以指定demicorrectId

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Debugging Test</title>
</head>

<body>
    <p id="demo">Click the button to display greeting.</p>
    <button onclick="myFunction()">Display Greeting</button>
    <script>
        function myFunction() {
            let id = document.getElementById("demo").id;
            let correctId = "demi";
            if (id === correctId) {
                document.getElementById("demo").innerHTML = "Hello World";
            } else console.log("ID doesn't match, id is", id);
        }
    </script>
</body>

</html>

image.png 现在,我们就获得了一个log, ID doesn’t match, id is demo。使用这个信息,开发者知道他们给按钮指派错了id 2. 警告

有时候控制台里太多的log,很难快速定位到问题。为了让重要信息脱颖而出,可以使用 console.warn() 方法。这个方式跟console.log()很类似,但是它以警告的方式输出日志。

同样的例子,我们使用console.warn()替代console.log() 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Debugging Test</title>
</head>

<body>
    <p id="demo">Click the button to display greeting.</p>
    <button onclick="myFunction()">Display Greeting</button>
    <script>
        function myFunction() {
            let id = document.getElementById("demo").id;
            let correctId = "demi";
            if (id === correctId) {
                document.getElementById("demo").innerHTML = "Hello World";
            } else console.warn("ID doesn't match, id is", id);
        }
    </script>
</body>

</html>

image.png 3. 错误

有时候,异常日志能更容易被关注到。我们可以使用console.error()方法做到。同样,使用console.error()来改变上面的例子:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Debugging Test</title>
</head>

<body>
    <p id="demo">Click the button to display greeting.</p>
    <button onclick="myFunction()">Display Greeting</button>
    <script>
        function myFunction() {
            let id = document.getElementById("demo").id;
            let correctId = "demi";
            if (id === correctId) {
                document.getElementById("demo").innerHTML = "Hello World";
            } else console.error("ID doesn't match, id is", id);
        }
    </script>
</body>

</html>

image.png 4. 断言 如果断言失败,console.assert()将会在控制台写一个异常。同样,改造一下我们的例子:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Debugging Test</title>
</head>

<body>
    <p id="demo">Click the button to display greeting.</p>
    <button onclick="myFunction()">Display Greeting</button>
    <script>
        function myFunction() {
            let id = document.getElementById("demo").id;
            let correctId = "demi";
            const errorMsg = "the ID doesn't match the correct ID";
            console.assert(id === correctId, { errorMsg: errorMsg });
            if (id === correctId) {
                document.getElementById("demo").innerHTML = "Hello World";
            }
        }
    </script>
</body>

</html>

image.png 5. Debugger 声明

debugger用来触发一个debbugger或者暂停代码的执行。它用来给你的应用设置断点。调试的时候使用断点是非常棒的,因为它允许开发者在应用执行时探查程序。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Debugging Test</title>
</head>
<body>
    <p id="demo">Click the button to display greeting.</p>
    <button onclick="myFunction()">Display Greeting</button>
    <script>

     function myFunction() {

          let id = document.getElementById("demo").id;

          let correctId = "demi";

          debugger;

          document.getElementById("demo").innerHTML = "Hello World";

}
    </script>
</body>

</html>

image.png

使用调试器来调试JS

调试工具也是调试器,他们提供了增强的可视化和分析,帮助开发者快速定位和修复bug。我们看看Firefox的调试工具把:

Firefox JS Debugger

Firefox的JS调试器,允许你在本地和远程调试你的JS应用。你可以设置断点,事件,调试线程等等。

开始调试

首先打开你的调试器,可以使用快捷键 CTRL + SHIFT + I ,然后切换到debugger面板。这样,调试器就打开了 注意,调试面板被分为3个部分,第一部分包含了页面必须的文件,第二部分包含了开发者感兴趣的文件(也就是你要调试的文件),第三部分包含调试需要的工具。

image.png 为了打开我们的文件,在第一个面板点击file:// 文件夹,然后选择选择你关系的文件、

暂停执行

调试时,断点会暂停代码执行,这一节中,我们会看看不同类型的断点

无条件断点

这一节我们使用console.error()的例子。当你点击文件的行数位置,就会添加一个断点,这就是一个无条件的断点。你也可以使用右键菜单来Add breakpoint

我们在第16行添加一个无条件断点,如果 (id === correctId),你就会得到一个“Paused at execution”的提示

image.png 现在,你就可以单步调试你的代码了。在调试过程中,可能有一些意外的日志。例如,在第19行,你将会得到异常信息。注意,不能在HTML代码中添加断点。

条件断点

当你定义的表达式值为true时,你的断点才会生效,这叫条件断点。例如,在16行:if (id === correctId) {添加一个下面的条件,就不会引起中断,因为idcorrectId不一致

id != correctId

image.png 如果我们改变了id === correctId的条件,就不会触发中断。

事件监听断点

开发者还可以给断点或者异常暂停添加事件监听器。在第三个面板中打开Event Listener Breakpoints 选项,然后点击你选择的任意的事件监听器。

如果你想无论任何时候程序发生异常,都可以执行暂停,选择“Pause on Exceptions”。

单步调试

代码暂停时,就可以单步调试,观察异常。你可以step over, step in, 或者 step out。 step over是执行函数内下一行代码,但不会进入子函数。 Step in 遇到子函数就进入。 Step OUT会执行完当前的函数

调试异步代码的提示

异步调试跟同步调试有点不一样。 异步代码在主线程执行之后才运行,不会阻塞JS代码的运行。在这个过程中,如果超时或者无响应,就会有异常。

调试异步JS代码,需要记住这些提示:

  1. 发生超时异常,可能是代码也可能是网络连接引起。要从提升超时事件和检查网络连接入手。
  2. 异步代码的数据不是按顺序的,调试时选择“Pause on Exception” 按钮。当你获取异常时就会暂停执行。使用step over 来探查整个调用栈。
  3. 异步调试的第一步是在不同的代码片段中使用console.log() 方法
  4. 使用try 和 catch声明来处理异常