node端如何处理错误?

时间:2020-11-21
本文章向大家介绍node端如何处理错误?,主要包括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.

上一篇:Java-for应用

下一篇:十大排序算法串讲