zoukankan      html  css  js  c++  java
  • node端如何处理错误?

    JavaScript的任何throw机制的使用都会引起异常,异常处理必须用try/catch来进行处理,否则nodejs进程会立即退出。

    同步的API会使用throw来报告错误。

    但是异步的API可能使用多种方法来报告错误---大多数异步API发生错误,采用callback方式来处理异常,其中callback的第一个参数就是err,如果第一个参数为null,而非err的话,则正确执行后面指令,反之为error的话,就会处理相应错误。

    如下所示:
    const fs = require('fs'); fs.readfile('a file doesn`t exist', (err, data)=>{ if(err){ console.log('readfile failed', err); } })

    ---当一个异步的方法被```EventEmitter```调用时候,错误会被分发到对象的```error```事件上```
    const net = require('net');
    const connection = net.connect('localhost');
    
    // 添加一个 'error' 事件句柄到一个流:
    connection.on('error', (err) => {
        // 如果连接被服务器重置,或无法连接,或发生任何错误,则错误会被发送到这里。 
        console.error(err);
    });
    
    connection.pipe(process.stdout);
    ``````error```的事件机制常见于基于流和基于事件触发器的API,它们本身就代表了一系列异步操作。
    对于所有的```EventEmitter```对象,如果没有提供一个```error```句柄,随后会抛出一个错误,nodejs进程会立即崩溃,
    除非:适当地使用```domain```模块或已经注册了一个 ```process.on('uncaughtException')```事件的句柄。```
    const EventEmitter = require('events');
    const ee = new EventEmitter();
    
    setImmediate(() => {
      // 这会使进程崩溃,因为还为添加 'error' 事件句柄。
      ee.emit('error', new Error('这会崩溃'));
    });
    ```

    一个通用的JavaScript的error对象,不会说明错误发生的具体情况,Error对象会捕捉一个"堆栈跟踪",详细的说明错误在代码中的具体位置,并且为错误提供文字描述。

    自定义错误(Customer Errors)

    对于Nodejs抛出的error,由于在实际工程中,如果一个一个的去定义错误,效率太低,因此可以利用类别的思想。在此基础上可以将错误具体分为HttpError,对于数据库操作的错误可以分为DbError,对于Searching Operations的操作错误分成NotFoundError。
    对于我们要自定义的错误,必须要有一个思想,这些自定义的错误必须要有基本的信息,比如name,message以及stack等,但是这些自定义的错误也要有属于自身的特性。
    对于这些自定义的错误,我们最好通过inherit from Error,这样以来,我们就可以通过obj instanceof Error来确认错误对象。
    当我们一步一步创建我们的应用的时候,我们自定义的错误自然而然的就会慢慢形成一个等级阶层,比如HttpTimeoutError也许就是继承自HttpError。

    Extending Error

    假设我们目前有一个函数,readUser(JSON),这个函数可以通过JSON形式读取用户信息。
    从一个JSON对象开始,如下
    let json = `{"name": "John", "age": 20}`
    一般而言,我们使用JSON.parse来解析,但是如果收到了错误的JSON格式,则会报出SyntaxError的错误,但是即使是JSON合法的,也不代表是有效的用户,有可能遗失必要的信息,假设所有要求的信息都有,但是不一定有效,我们之前的函数readUser(JSON)不仅仅会读取,而且会验证数据的正确性,如果是不合法的JSON格式,则会抛出SyntaxError,对于另一种错误,比如验证信息和数据库的信息不匹配,此时就应该抛出ValidationError,此时的ValidationError``应该是继承自Error,具体关于ValidationError```的代码如下

    // The "pseudocode" for the built-in Error class defined by JavaScript itself
    class Error {
      constructor(message) {
          this.message = message;    
          this.name = "Error"; // (different names for different built-in error classes)
        this.stack = <nested calls>; // non-standard, but most environments support it
      }
    }

    Validation的大类继承于Error

     class ValidationError extends Error { 
          constructor(message) {
                  super(message); // (1)
            this.name = "ValidationError"; // (2)
          }
        }    
        function test() {
              throw new ValidationError("Whoops!");
        }    
        try {
          test();
        }
         catch(err) {
          alert(err.message); // Whoops!
          alert(err.name); // ValidationError
          alert(err.stack); // a list of nested calls with line numbers for each
        }

    我们将Validation用在readUser里面

    class ValidationError extends Error {
      constructor(message) {
          super(message);    
          this.name = "ValidationError";
      }
    }// Usage
    function readUser(json) {
      let user = JSON.parse(json);  
      if (!user.age) {
          throw new ValidationError("No field: age");
      }
        if (!user.name) {
            throw new ValidationError("No field: name");
      }  
      return user;
    }
    // Working example with try..catch
    try {
      let user = readUser('{ "age": 25 }');
    }
     catch (err) {
       if (err instanceof ValidationError) {
        alert("Invalid data: " + err.message); // Invalid data: No field: name
      } else if (err instanceof SyntaxError) { // (*)SyntaxError是内置在JSON.parse()中的
        alert("JSON Syntax Error: " + err.message);
      } else {
          throw err;   // unknown error, rethrow it (**) catch仅仅知道如何去处理已经定义的错误,
                      //遇到未定义的错误的时候,让其直接fall through
      }
    }

    对于Validation来说,属于非常基本的类别,也许会出现本来应该在年龄那里填写数字,但是传进去的值却不是数字,此时就会有粒度更细的class来分别这类错误,比如说PropertyRequiredError

    class PropertyRequiredError extends ValidationError {
      constructor(property) {
          super("No property: " + property);    
          this.name = "PropertyRequiredError";    
          this.property = property;
      }
    }

    但是每次定义this.property = property很麻烦,每创建一个类别的时候就需要定义一次,我们可以将this.constructor.name赋值于this.name,定义为MyError

     class MyError extends Error {
           constructor(message) {
                   super(message);        
                   this.name = this.constructor.name;
          }
        }    
        class ValidationError extends MyError { }    
        class PropertyRequiredError extends ValidationError {
              constructor(property) {
                      super("No property: " + property);        
                      this.property = property;
          }
        }    
        // name is correct
        alert( new PropertyRequiredError("field").name ); // PropertyRequiredError

    此时定义错误的代码就会短了很多。

    广州品牌设计公司https://www.houdianzi.com

    Wrapping Exception

    随之具体情况的细分下去,readUser可能要处理的错误会越来越多,但是针对每一个错误,都得加一个if...try/catch明显效率太低。
    目前这种情况下,创建一个ReadError的类别,用来代表一类错误的类别,如果一个错误发生在ReadError里,此时,我们会将错误捕获,并且抛出ReadError,此时我们仍会在cause里面对原始错误信息保持追踪,外层代码就仅仅需要去检查ReadError就可以了。以下是示例

    class ReadError extends Error {
      constructor(message, cause) {
          super(message);    
          this.cause = cause;    
          this.name = 'ReadError';
      }
    }
    class ValidationError extends Error { /*...*/ }
    class PropertyRequiredError extends ValidationError { /* ... */ }
    function validateUser(user) {
      if (!user.age) {
          throw new PropertyRequiredError("age");
      }
        if (!user.name) {
            throw new PropertyRequiredError("name");
      }
    }
    function readUser(json) {
      let user;  try {
        user = JSON.parse(json);
      }
        catch (err) {
            if (err instanceof SyntaxError) {
                  throw new ReadError("Syntax Error", err);
        } else {
              throw err;
        }
      }
         try {
        validateUser(user);
      
      }
       catch (err) {
           if (err instanceof ValidationError) {
                 throw new ReadError("Validation Error", err);
        } else {
              throw err;
        }
      }
    
    }try {
      readUser('{bad json}');
    } catch (e) {
      if (e instanceof ReadError) {
        alert(e);    // Original error: SyntaxError: Unexpected token b in JSON at position 1
        alert("Original error: " + e.cause);
      } else {    throw e;
      }
    }

    按照以上代码,外层结构的代码就只需要去检查instanceof ReadError,而不需要去将所有的情况罗列出来了。
    这种方式名称叫做Wrapping Exception.

  • 相关阅读:
    MyBatis的几种使用形式。
    laradock开发环境搭建笔记
    前端如何引入vConsole
    技术更迭,一往无前
    博客园使用markdown
    python telnetlib 模块实现功能端口扫描
    kubernetes之ingress部署
    kubernetes之ingress error: endpoints "default-http-backend" not found
    mvc 查询条件回显
    BootStrap 下来菜单点击两次才弹框问题
  • 原文地址:https://www.cnblogs.com/xiaonian8/p/14017525.html
Copyright © 2011-2022 走看看