koa2中间件的错误捕捉与async/await本质

在eggjs项目中官方建议使用中间件来捕获,在前面的文章中介绍过来koa2中间件原理。

try...catch

当时只注意了其执行原理本质上只是递归。但是忽略了try...catch这层代码的作用。

当中间件是同步函数时:

直接运行fn就会被try..catch捕获,从而终止后面的中间件执行。

如果fn成功执行,再利用Promise.resolve来保证返回的是一个promise实例。

当中间件是async函数时:

中间件返回Promise实例,并将其作为参数传入Promise.resolve函数。

Promise.resolve函数有一个特性就是当参数是一个Prmoise实例的时候,会等到此实例被决议才会执行resovle函数,即,等待async函数完全执行完。

这就是koa中间件的核心了:多个异步中间件调用,当前中间件函数体内使用了await next()的写法,会让当前中间件一直等到下个中间件异步执行完毕后再执行自身后续逻辑。

async的实现与错误捕捉

在下方的run函数就是async实现的原理。在run函数中使用try...catch用以捕获错误。

这里是考虑到如果在生成器函数中没有用try...catch捕获错误,那么就在run函数中使用try...catch捕获task.throw(err)的错误并结束生成器函数运行。

但是,如果在生成器函数内用try...catch捕获了task.throw(err)的错误,生成器函数后续的代码会继续执行。

生成器函数如何捕获状态为onRejected的Promise实例?

当生成器函数 yield/await 返回一个onRejected状态的promise实例时,run函数中的result.value为promise,run函数中的promise是不会捕获这个错误的。

但是,接下来把resulte.value,即,状态为onRejected的Promise实例传入Promise.resolve,Promise.resolve会调用catch内的回调函数。

而catch内的回调函数调用了task.throw,在下一次调用step函数时,如果生成器函数内部没有使用try...catch捕获task.throw,那么就会使用step函数内部的try...catch,并提前终止生成器函数执行。

实例

下面为几个实例

中间件统一处理错误

多个中间件错误捕捉:

中间件1:错误捕捉

中间件2:鉴权

中间件3:其他中间件,抛出错误

输出:

auth before 

test before 

catch error

现象分析

理论上中间件顺序为:error -> auth -> test。由于函数调用栈的原因,会形成一个洋葱圈模型。但是,现在的情况是test执行报错了,因此行为有点不一样。

由实力的输出结果可知:

1. 在第一个中间件中可以捕捉到后面中间件的错误

2. 在每个中间件函数await next()之后的代码并没有执行,即,洋葱圈只剩下一半。

原理分析

1. 在test中间件中抛出了错误,由async的实现原理可知,此中间件返回的是一个onRejected状态的Promise实例,且test函数代码并没有执行完,即,不会执行下一次迭代,那么就终止调用test之后的所有中间件。

2. 再结合koa中间件原理:return Promise.resolve(fn(context, diapatch.bind(null, i+1)))

auth中间件的await next()会等到test执行结束,而test提前终止,并且test返回一个onRejected状态的Promise,导致auth的执行到await next()时也会提前终止,并返回一个新的onRejected状态的Promise

error中间件的await next()也会等到auth执行结束,而同样地,auth提前终止并返回另一个新的onRejected的Promise。

最后再在error中间件函数中采用try...catch捕捉到这个onRejected状态的promise,并对此错误做一个处理。

总结

由async实现原理可知,

1. 在async函数内部可以使用try...catch捕捉一个onRejected状态的Promise实例

2. 在async函数内部不使用try...catch捕捉错误,那么产生错误时候,async函数提前终止,并返回 一个onRejected状态的Promise

由koa中间件原理可知,

中间件函数内await  next()本质上就是递归调用dispatch(i+1)。

1. 如果下一个中间件内部没有使用try...catch捕捉错误,中间件函数就会返回一个onRejected状态的Promise实例,dispatch。提前终止中间件执行。

2. 如果,中间件内部使用了try...catch捕捉错误,则函数会完全执行完。

最后

最后一篇与中间件有关的文章了,,,我觉得我真的懂了.....

原文链接:juejin.im

上一篇:Express中间件原理解析与实现
下一篇:高程4中模拟事件触发

相关推荐

  • 🙋Hanjst汉吉斯特升级:+showImageAsync及性能改进等

    自2019年元旦🙋Hanjst汉吉斯特 模板语言及其编译引擎发布,已经过去一年多了。 这期间随着 🙋Hanjst汉吉斯特 的推广应用,我们也陆续发布了如下一些更新内容: 🛠️Hanjst/汉吉...

    6 个月前
  • 🔥 Promise|async|Generator 实现&原理大解析 | 9k字

    笔者刚接触async/await时,就被其暂停执行的特性吸引了,心想在没有原生API支持的情况下,await居然能挂起当前方法,实现暂停执行,我感到十分好奇。好奇心驱使我一层一层剥开有关JS异步编程的...

    7 个月前
  • 面试还问redux?那我从头手撸源码吧(中间件篇)

    昨天的文章手写了一版redux的核心源码,redux库除了数据的状态管理还有一块重要的内容那就是中间件,今天我还是尝试将此部分源码完成。 中间件 react中管理数据的流程是单向的,就是说,从派发动作...

    2 年前
  • 面试官: 说说你对async的理解

    大家好,我是小雨小雨,致力于分享有趣的、实用的技术文章。 内容分为翻译和原创,如果有问题,欢迎随时评论或私信,希望和大家一起进步。 分享不易,希望能够得到大家的支持和关注。

    6 个月前
  • 重新理解async和await

    async 和 await 在干什么 任意一个名称都是有意义的,先从字面意思来理解。async 是“异步”的简写,而 await 可以认为是 async wait 的简写。

    1 年前
  • 重学JS:async/await

    前言 异步操作一直是JS中不可或缺的一环,从最开始回调函数,到后面的Promise,再到ES2017引入的async函数,异步操作逐渐进化,变得越来越简单方便,接下来就仔细看看在ES2017引入了as...

    1 年前
  • 通过编写一个路由中间件来学习 Koa

    混了四年的大学生活结束了,校招没有找到工作的我还面临着失业。没办法,只有临时抱抱佛脚看看能不能找个工作了。据说最近前端圈里不会 NodeJs 是不可能找到工作的,于是抱起了 NodeJs 里比较流行的...

    2 年前
  • 通过变量来使用next()在expressjs未来中间件

    cchamberlainuser2791897提出了一个问题:passing variables to the next middleware using next() in expressjs,或许...

    3 年前
  • 谈谈 <script> 标签以及其加载顺序问题,包含 defer & async

    谈谈 &lt;script&gt; 标签加载顺序的问题 这篇文章比较长,如果你耐心读完了,你一定会感谢此时决定读下去的你。 如果这不是一篇有用的关于&lt;script&gt; 标签文章的话,记...

    3 年前
  • 详谈Ajax请求中的async:false/true的作用(ajax 在外部调用问题)

    test.html &lt;a href="javascript:void(0)" rel="external nofollow" onmouseover="testAsync()"&gt; asy....

    4 年前

官方社区

扫码加入 JavaScript 社区