如何优雅的在 koa 中处理错误

714 查看

如何优雅的在 koa 中处理错误

软件开发时,有 80% 的代码在处理各种错误。

——某著名开发者

想让自己的代码健壮,错误处理是必不可少的。这篇文章将主要介绍 koa 框架中错误处理的实现(其实主要是 co 的实现),使用 koa 框架开发 web 应用时进行错误处理的一些方法。

基础

在 Node.js 中,错误处理的方法主要有下面几种:

  • 和其他同步语言类似的 throw / try / catch 方法
  • callback(err, data) 回调形式
  • 通过 EventEmitter 触发一个 error 事件

第一种使用 catch 来捕获错误,十分易用,其他两种在捕获错误时多多少少都有些别扭。

但是 koa 通过十分巧妙的”黑魔法“让我们可以使用 catch 来捕获异步代码中的错误。比如下面的例子:

在 koa 中,推荐统一使用 throw / try / catch 的方式来进行错误的触发和捕获,这会让代码更加易读,防止被绕晕。

原理

上面我们说了 koa 中可以使用 try / catch,我们就来分析下它是如何做到的。koa 基于 co,所以,我们其实主要是分析 co 的实现。(注:这一部分比较偏原理,不关心的可以跳过。)

首先,我们来看看什么是 generator。

带有 * 的函数声明表示是一个 generator 函数,当执行 gen() 时,函数体内的代码并没有执行,而是返回了一个 generator 对象。

generator 函数通常和 yield 结合使用,函数执行到每个 yield 时都会暂停并返回 yield 的右值。下次调用 next 时,函数会从 yield 的下一个语句继续执行。等到整个函数执行完,next 方法返回的 done 字段会变成 true,并且将函数返回值作为 value 字段。

第一次执行 next() 时,走到 yield 'start' 后暂停并返回 yield 的右值 'start'。注意,此时var a = 这个赋值语句其实还没有执行。

第二次执行 next(22) 时,从 yield 'start' 下一个语句执行。于是执行 var a = 这个赋值语句,而表达式 yield 'start' 的值就等于传递给 next 函数的参数值 22,所以,a 被赋值为 22。然后继续往下执行到 yield 'end' 后暂停并返回 yield 的右值 'end'

第三次执行 next(333) 时,从 yield 'end' 下一个语句执行。此时执行 var b = 这个赋值语句,表达式 yield 'end' 的值等于传递给 next 函数的参数 333b 被赋值为 333。继续往下执行到 return 语句,将 return 语句的返回值作为 value 返回,因为函数已经执行完毕,done 字段标记为 true

可以看到 generator 就是一种迭代机制,就像一只很懒的青蛙,戳一下(调用 next)动一下。

generator 对象还有一个 throw 方法,可以在 generator 函数外面抛出异常,然后在 generator 函数里面捕获异常。有点绕?我们来看一个实例:

我们执行一次 next,会运行到 yield 'a' 这里然后暂停,这一句刚好在 try 的返回内,因此 it.throw 抛出的错误我们可以 catch 到。并且看到 throw 返回的 done 字段是 true,说明后面的 yield 'b' 已经不会再执行了。

如果我们不调用 next,或者连续调用三次 nextyield 代码不在 try 返回里面,会导致报错。co 的错误处理其实正是利用了这个 throw 方法。

下面我们来看看 co 的核心代码: