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等,但是这些自定义的错误也要有属于自身的特性。
对于
来源gao!%daima.com搞$代*!码网
这些自定义的错误,我们最好通过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
此时定义错误的代码就会短了很多。
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.
以上就是node端如何处理错误?的详细内容,更多请关注gaodaima搞代码网其它相关文章!