JavaScript中正确的错误处理

Every programmer out there wants to write code that works. In the long run, chances of your program running as expected reduce due to one thing ‒ errors. Errors in JavaScript could get complex at certain times and when that happens, programmers have no one to blame but the themselves or the language itself. It would be great if we all knew how to detect these errors, expose them and prevent them from happening again. One thing to note though, this article assumes some knowledge in JavaScript. If you’re looking to enhance your skills, why not sign up with Scotch and watch our course on JavaScript. It’s completely free. My objective is to show you how JavaScript errors can be handled properly and avoided in your code. When you are done, you won’t be scared of frequently implementing features such as the stack property as well as handling errors.

每个在那里的程序员都想编写有效的代码。 从长远来看,由于一件事的错误,您的程序按预期运行的机会会减少。 JavaScript的错误有时会变得很复杂,一旦发生这种错误,程序员除了人本身或语言本身外,没有人要责怪。 如果我们都知道如何检测这些错误,暴露它们并防止它们再次发生,那就太好了。 不过要注意的一件事是,本文假设您对JavaScript有一定的了解。 如果您想提高自己的技能,为什么不注册Scotch并观看我们的JavaScript课程。 它是完全免费的。 我的目标是向您展示如何正确处理JavaScript错误并在代码中避免这些错误。 完成后,您将不会害怕频繁实现诸如stack属性以及处理错误之类的功能。

错误类别 (Categories of Errors)

Usually JavaScript errors can be divided into two categories, actual problems and programmer mistakes. Actual problems are events that are not preventable by the programmer. For example, a program asking a user to enter a name and it gets back an empty string.

通常,JavaScript错误可以分为两类:实际问题和程序员错误。 实际问题是程序员无法避免的事件。 例如,一个程序要求用户输入名称,然后它返回一个空字符串。

Programmer mistakes are errors made by the programmer when writing programs. They can be further subdivided into:

程序员错误是程序员在编写程序时所犯的错误。 它们可以进一步细分为:

语法错误 ( Syntax Errors )

These are the most basic kind of errors. Also known as parsing errors, they occur at compilation time in traditional programming languages and interpretation time in JavaScript. Check out the code block below:

这些是最基本的错误。 也称为解析错误,它们在传统编程语言的编译时和JavaScript的解释时发生。 查看下面的代码块:

var x = 2;
  var y = 3;

  console.log(x + y;

In the example above, the last line of code will cause a syntax error because it is missing a closing parenthesis. Usually when a syntax error occurs in JavaScript, the rest of the code in other threads will get executed if they contain nothing that depends on the code containing the error.

在上面的示例中,最后一行代码将导致语法错误,因为它缺少右括号。 通常,当JavaScript中发生语法错误时,如果其他线程中的其余代码不包含任何内容(取决于包含该错误的代码),则其余代码将被执行。

运行时错误 ( Runtime Errors )

Also known as exceptions, these kind of errors occur when your program is executed, usually when it has been compiled or interpreted. Consider the code block below:

也称为异常,在执行程序时通常会在编译或解释程序时发生此类错误。 考虑下面的代码块:

var windowObject;
  var windowFeatures = "menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes";

  function openPopup() {
    windowObject = window.openObject("http://www.bbc.com/", "BBC_WindowName", strWindowFeatures);
  }

The function above will cause a runtime error because although the syntax is correct, at runtime it is trying to call a method openObject() that doesn’t exist.

上面的函数将导致运行时错误,因为尽管语法正确,但在运行时它将尝试调用不存在的方法openObject()

逻辑错误 ( Logical Errors )

The most complex of all three errors, logical errors occur when you make a mistake or flaw in the logic that controls your program’s script. This mistake causes the program to alter expected results or exhibit unexpected behavior. It’s usually very tricky trying to catch and even correct logical errors, consider the example below:

这是三种错误中最复杂的一种,当您在控制程序脚本的逻辑中犯错或出现缺陷时,就会发生逻辑错误。 此错误导致程序更改预期结果或表现出意外行为。 尝试捕获甚至纠正逻辑错误通常非常棘手,请考虑以下示例:

function power(base, exponent) {
    var result = 1;
    for (var count = 0; count < exponent; count++)
      result *= base;
    return result;
  }

What if someone tries to call power (``"``JavaScript``"``, 10)? Well that’s obviously a programmer mistake. What about power (5, 0.2)? A quick look at the function’s logic will tell you that it can’t handle fractional exponents but by the law of Mathematics, raising a number to the halfth power is possible. JavaScript does this via the Math.pow() function. In such situations where it’s not entirely clear what kind of input a function accepts, explicitly stating the kind of arguments that are acceptable in a comment or input validation becomes a good idea.

如果有人尝试调用电源(``"``JavaScript``"``, 10)怎么办? 好吧,这显然是程序员的错误。 功率(5, 0.2)呢? 快速浏览该函数的逻辑将告诉您它不能处理小数指数,但根据数学定律,可以将数字提高到一半。 JavaScript通过Math.pow()函数执行此操作。 在尚不完全清楚函数接受哪种输入的情况下,明确说明注释或输入验证中可接受的参数类型是个好主意。

处理错误的最佳做法 (Best Practices for Handling Errors)

One common criticism of JavaScript is the way errors are handled through callbacks. Check out the pattern in which the code below is written:

对JavaScript的一种普遍批评是通过回调处理错误的方式。 查看以下代码的编写模式:

var myFunc = function(cb) {
    doSomething(function (err, a) {
      if (err) return cb(err)
      doSomethingElse(function (err, b) {
        if (err) return cb(err)
        return cb(null, [a, b])
      })
    })
  }

What’s good about this kind of pattern? It forces programmers to handle errors. As the person writing the code, you always want to make sure you know when an operation can fail, especially if it’s an asynchronous operation.

这种模式有什么好处? 它迫使程序员处理错误。 作为编写代码的人,您总是想确保自己知道什么时候操作会失败,特别是如果它是异步操作。

What’s bad about this kind of pattern? It seems like a lazy way of writing code. Asides that, if part of the code fails, the entire operation will fail. Certain exceptions to this are retry logic, reverting changes, and advanced error reporting.

这种模式有什么不好? 似乎是编写代码的一种懒惰方式。 假设,如果部分代码失败,则整个操作将失败。 重试逻辑,还原更改和高级错误报告是这种情况的某些例外。

Fortunately, there are quite a number of ways and methods to deal with errors and exceptions. Let’s check out these methods and how they can be applied to our code:

幸运的是,有许多方法和方法可以处理错误和异常。 让我们看看这些方法以及如何将它们应用于我们的代码:

尝试…抓住…最后声明 ( The try…catch…finally Statement )

The try…catch…finally statement marks a block of statements to run in your code and specifies a response should an exception be thrown. It s possible to catch logical and runtime errors but not syntax errors. Below is the syntax for the try…catch…finally statement:

try…catch…finally语句标记了要在您的代码中运行的语句块,并指定了引发异常时的响应。 可以catch逻辑和运行时错误,但不能catch语法错误。 下面是try…catch…finally语句的语法:

try {
    // Code to run
    [
      break;
    ]
  } catch (e) {
    // Code to run if an exception occurs
    [
      break;
    ]
  }
  [
    finally {
      // Code that is always executed regardless of 
      // an exception occurring
    }
  ]

try statements are the statements to be executed. If an exception occurs during the execution of the try statement, the exception is placed in e and the catch clause is executed otherwise, the catch clause is skipped. The finally clause executes after the try statement is finished, it executes regardless of whether or not an exception was thrown or caught. try statements can be used either with just the catch clause, just the finally clause or both. Check out this demo using just the try statement and the catch clause:

try语句是要执行的语句。 如果在try语句执行期间发生异常,则将该异常放在e并执行catch子句,否则,将跳过catch子句。 try语句完成后, finally子句执行,无论是否引发或捕获异常,它都会执行。 try语句既可以仅与catch子句一起使用,也可以仅与finally clause或者两者都可以使用。 仅使用try语句和catch子句查看此演示:

Using the finally clause allows you to execute an additional command after try…catch. Here’s an example:

通过使用finally子句,可以在try…catch之后执行其他命令。 这是一个例子:

投掷声明 ( The Throw Statement )

The throw statement is used to generate user-defined exceptions. During runtime, when a throw statement is encountered, execution of the current function will stop and control will be passed to the first catch clause in the call stack. If there is no catch clause, the program will terminate. Check out this example showing how to use a throw statement:

throw语句用于生成用户定义的异常。 在运行时,当遇到throw语句时,当前函数的执行将停止,并将控制权传递给调用堆栈中的第一个catch子句。 如果没有catch子句,程序将终止。 查看此示例,该示例显示如何使用throw语句:

onerror()方法 ( The onerror() Method )

The onerror() method was the first event handler to facilitate and handle errors in JavaScript. It is often used with the syntax window.onerror. This enables the error event to be fired on the window object whenever an error occurs during runtime. Below is an example showing how to use the onerror() method:

onerror()方法是第一个促进和处理JavaScript错误的事件处理程序。 它通常与语法window.onerror 。 这样,只要在运行时发生错误,就可以在窗口对象上触发错误事件。 下面是显示如何使用onerror()方法的示例:

Another utility mode for onerror() is using it to display an error message in case there is any error when loading images in your site:

onerror()另一种实用程序模式是使用它来显示错误消息,以防在站点中加载图像时出现任何错误:

<img src="coolPhoto.jpg" onerror="alert('An error occurred loading yor photo.')" />

调用堆栈属性 ( Call Stack Property )

The stack property is a feature in JavaScript Error object. It offers a trace of which functions were called, in what order, from which line and file and with what arguments, proceeding from the most recent calls to earlier ones all the way to the original global scope call. Check out this code block demonstrating the stack property:

stack属性是JavaScript错误对象中的功能。 它提供了跟踪的信息,以什么顺序,从哪个行和文件,以什么参数调用了哪些函数,从最近的调用到较早的调用一直到原始的全局范围调用。 签出演示stack属性的此代码块:

function trace() {
    try {
      throw new Error('myError');
    } catch (e) {
      alert(e.stack);
    }
  }

  function b() {
    trace();
  }

  function a() {
    b(3, 4, '\n\n', undefined, {});
  }
  a('first call, firstarg');

Assuming the above markup is saved as C:\stackoverflow.js on a Windows file system it produces an alert message box with the following text:

假设以上标记在Windows文件系统上另存为C:\stackoverflow.js ,则会生成带有以下文本的警报消息框:

trace@file:///C:/stackoverflow.js:4:17
  b@file:///C:/stackoverflow.js:11:13
  a@file:///C:/stackoverflow.js:14:13
  @file:///C:/stackoverflow.js:15:9

A warning though, stack is a non standard feature. It shouldn’t be used on production sites facing the web as it will not work for every user.

但是,警告是stack是非标准功能。 不应在面向网络的生产站点上使用它,因为它不适用于每个用户。

处理异步代码中的错误 (Handling Errors in Asynchronous Code)

Usually errors in asynchronous code require a large amount of if… else checks and a careful inspection of parameter values. Promises allow asynchronous code to apply structured error handling. When using promises, you can process errors by passing an error handler to the then method or using a catch clause. Just like exceptions in regular code, an exception or rejection in asynchronous code will jump to the nearest error handler. Check out the code block below:

通常,异步代码中的错误需要大量的if… else检查和对参数值的仔细检查。 承诺允许异步代码应用结构化错误处理。 使用Promise时,可以通过将错误处理程序传递给then方法或使用catch子句来处理错误。 就像常规代码中的异常一样,异步代码中的异常或拒绝将跳转到最近的错误处理程序。 查看下面的代码块:

var log = "";

  function doWork() {
    log += "W";
    return Promise.resolve();
  }

  function doError() {
    log += "E";
    throw new Error("oops!");
  }

  function errorHandler(error) {
    log += "H";
  }

These functions were created to show you how to process errors using the then method. We’ll use them with the following code:

创建这些功能是为了向您展示如何使用then方法处理错误。 我们将它们与以下代码结合使用:

doWork()
    .then(doWork)
    .then(doError)
    .then(doWork) // this will be skipped
    .then(doWork, errorHandler)
    .then(verify);

  function verify() {
    expect(log)
      .toBe("This");
    done();
  }

What’s expected is that the log variable will contain “WWEH” when the code finishes executing, meaning the flow of calls with reach doWork , then doWork, then doError, then errorHandler. There are two things we can observe from this. The first is that when the call to doError throws an exception, execution jumps to the next rejection handler which is errorHandler and skips over any potential success handlers. This behavior is obvious once you think of promises as a tool to transform asynchronous code into a procedural flow of method calls. In synchronous code, an exception will jump over statements and up the stack to find a catch handler, and the asynchronous code in this example is no different.

可以预期的是,在代码完成执行后,日志变量将包含“ WWEH”,这意味着到达范围为doWorkdoWorkdoErrorerrorHandler的调用流程。 我们可以从中观察到两件事。 首先是当对doError的调用引发异常时,执行将跳至下一个拒绝处理程序errorHandler并跳过所有可能的成功处理程序。 一旦将promise视为将异步代码转换为方法调用的过程流的工具,此行为就显而易见。 在同步代码中,异常将跳过语句并在堆栈中向上查找捕获处理程序,本示例中的异步代码没有什么不同。

The second observation is that the verify function will execute as a success handler after the error. Just like normal execution can resume in procedural code after a catch clause, normal execution can resume with promises after a handled error. The verify function executes because the error handler returns a successfully resolved promise. Remember that it’s the then method’s job to return a new promise, and unless the error handler explicitly rejects a new promise, the new promise resolves successfully.

第二个观察结果是, verify函数将在错误发生后作为成功处理程序执行。 就像正常执行可以在catch子句之后在程序代码中恢复一样,正常执行可以在处理了错误之后使用promise恢复。 verify函数之所以执行,是因为错误处理程序返回了成功解决的承诺。 请记住,返回新的诺言是then方法的工作,除非错误处理程序明确拒绝新的诺言,否则新的诺言将成功解析。

A promise object also provides a catch clause to handle errors. Check out this example which is written using a catch clause:

Promise对象还提供了catch子句来处理错误。 看看这个使用catch子句编写的示例:

doWork()
    .then(doWork)
    .then(doError)
    .then(doWork)
    .then(doWork)
    .catch(errorHandler)
    .then(verify);

The catch clause takes only a rejection handler method. There can be a difference in behavior between the following two code snippets:

catch子句仅采用拒绝处理程序方法。 以下两个代码段的行为可能有所不同:

.then(doWork, errorHandler)

… and …

……和……

.then(doWork)
  .catch(errorHandler)

In the first snippet, if the success handler throws an exception or rejects a promise, execution will not go into the error handler since the promise was already resolved at this level. With catch, you can always see an error that was not handled from the previous success handler. Finally, imagine you have a rejected promise in your code, but there is no error handler attached. You can simulate this scenario with the following line of code:

在第一个代码段中,如果成功处理程序引发异常或拒绝诺言,则执行将不会进入错误处理程序,因为诺言已在此级别得到解决。 使用catch ,您始终可以看到以前的成功处理程序未处理的错误。 最后,假设您的代码中有一个被拒绝的承诺,但是没有附加错误处理程序。 您可以使用以下代码行来模拟这种情况:

Promise.reject("error!");

Some native environments and promise polyfills will warn you about unhandled promise rejections by displaying a message in the console of the developer tools. An unhandled promise rejection could spell doom your application as it might be leaving a critical error unattended to.

某些本机环境和Promise填充会通过在开发人员工具的控制台中显示一条消息来警告您未处理的Promise拒绝。 未处理的承诺拒绝可能会给您的应用程序带来厄运,因为它可能会导致严重的人为错误。

结论 (Conclusion)

When it comes to handling errors as a programmer, you either choose to ignore them and play the pretend game or become a superhero and go back in time to save the world - or in this case, your code. Personally I would recommend becoming a superhero, ignoring errors will only lead to more and more errors until the entire stack becomes impossible to work on or even to attempt a bug fix. There’s no harm in trying, no shame in admitting failure. There will always be mistakes, it’s what we do about them that matters in the end.

当以程序员的身份处理错误时,您可以选择忽略它们并玩假装游戏,或者成为超级英雄并及时返回以拯救世界-在这种情况下,还可以是代码。 我个人建议成为超级英雄,忽略错误只会导致越来越多的错误,直到整个堆栈变得无法使用甚至无法修复错误为止。 尝试没有害处,承认失败也没有羞耻感。 总是会有错误,最后,我们对错误的处理才是最重要的。

翻译自: https://scotch.io/tutorials/proper-error-handling-in-javascript

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值