一种Nodejs中的错误处理的实践

Feb 20, 2014

我在使用Nodejs的过程中感觉她错误处理设计的非常不好。官方关于可能抛出的错误很少有文档。我大部分情况下都是调试的时候出现了错误,再去看错误码处理。这种方式让我开发效率大打折扣,同时我觉得这不是正确的设计程序的方法。

最近,我发现了一种比较不错的错误处理的实践。

nodejs的回调传统

nodejs的回调函数不论有多少个参数,一般第一个参数都是error。也就是当异步操作发生错误时的错误信息。 比如 coffee-script fs.readFile "./config.json","utf8",(err,string)-> if err console.error "fail to read config" # do some error handling console.log "read config" # do something with config

在上面的代码中err可能有很多种,比如没有访问的权限,又比如文件不存在,也许在硬件出现问题时,还会产生我不知道的错误。如果没有好的文档,我很难针对不同的错误做出正确的处理,比如给出正确的错误提示,然后要求用户换一个config文件。在这种氛围下,开发者很容易选择把头埋在沙子里,并让程序“优雅”的挂掉。实际上我支持优雅的挂掉,但我不想把头埋在沙子里。可是开源社区的程序员时间都非常宝贵,要求完整的文档是非常困难的。

书写能替代错误文档的模块

我们可以通过在模块的入口出定义所有错误的方式,让开发人人员在没有文档的情况下也能方便的知道有哪些错误。比如下面这个模块。

```coffee-script # configLoader.coffee

Errors = {} Errors.IOError:createError “IOError” Errors.InvalidParameter:createError “InvalidParameter” Errors.PermissionDenied:createError IOError,”PermissionDenied” Errors.NotExists:createError IOError,”NotExists” Errors.BrokenConfig:createError “BrokenConfig” class ConfigLoader constructor:()-> return true load:(path,callback)-> if not path or typeof path isnt “string” and not (path instanceof String) callback new exports.Errors.InvalidParameter “load config require, path parameter” return fs.readFile path,”utf8”,(err,configString)-> if err if err.code is “ENOENT” callback new exports.Errors.NotExists() else if err.code is “ACCES” callback new exports.Errors.PermissionDenied() else callback err return try data = JSON.parse configString catch e callback new exports.Errors.BrokenConfig() return callback null,data module.exports = ConfigLoader module.exports.Errors = Errors ``` 上面这种书写方式,对于模块的调用者来说,可以清楚的知道这个模块可能产生哪些Error,并且处理起来非常方便。

coffee-script ConfigLoader = (require "./configLoader") cl = new ConfigLoader() cl.load "./config.json",(err,config)-> if err instanceof ConfigLoader.Errors.IOError console.error "fail to load config file" return else if err instanceof ConfigLoader.Errors.BrokenConfig console.error "config file is not a valid json" return else # something beyond my ability to handle # die gracefully throw err

用createError方法有许多好处。确确实实的创建一个Error对象而不是返回一个string或者object作为error是一个好的实践,而且Error上面也会有stack的trace log在抛出时能够很好的帮助我们调试。而且对于同一种类型的Error有些我们可以合并处理。

你可以通过 npm install create-error 安装这个模块,并且通过createError = require "create-error"引用它。