20行代码简单实现koa洋葱圈模型示例详解

 更新时间:2023年01月17日 10:01:26   作者:秋染蒹葭  
这篇文章主要为大家介绍了20行代码简单实现koa洋葱圈模型示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

koa想必很多人直接或间接的都用过,其源码不知道阅读本文的你有没有看过,相当精炼,本文想具体说说koa的中间件模型,一起看看koa-compose的源码,这也是koa系列的第一篇文章,后续会更新一下koa相关的其他知识点

koa中间件的使用

先让我们启动一个koa服务

// app.js
const koa = require('koa');
const app = new koa();
app.use(async (ctx, next) => {
  console.log('进入第一个中间件')
  next();
  console.log('退出第一个中间件')
})
app.use(async (ctx, next) => {
  console.log('进入第2个中间件')
  next();
  console.log('退出第2个中间件')
})
app.use((ctx, next) => {
  console.log('进入第3个中间件')
  next();
  console.log('退出第3个中间件')
})
app.use(ctx => {
  ctx.body = 'hello koa'
})
app.listen(8080, () => {
  console.log('服务启动,监听8080端口')
})

上述的服务在访问的时候会得到如下结果:

服务启动,监听8080端口
进入第1个中间件
进入第2个中间件
进入第3个中间件
退出第3个中间件
退出第2个中间件
退出第1个中间件

上面的返回结果有点像一个递归的过程,从现象上看当中间件调用next()的时候,函数会暂停并进入到下一个中间件,当执行了最后一个中间件后,执行代码会回溯上游中间件,并执行next()之后的代码,这就是koa的核心能力,洋葱圈模型

洋葱圈模型

先看一张经典的洋葱圈模型的示意图:

在开发过程中,可以将next()之前的代码理解为捕获阶段, 而next()之后的代码可以理解为释放阶段,开发者结合这两个状态可以实现一些有趣的操作,比如记录一次请求的时间:

async function responseTime(ctx, next) {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
}
app.use(responseTime);

使用洋葱圈模型可以直接将响应时间记录的操作解耦出来,这样就不需要再去对称的写在业务逻辑中了,这是怎么实现的呢?

洋葱圈模型的实现,koa-compose

我们通过app.use()添加了很多函数,这些函数最终传递给了compose函数,先贴上koa-compose的源码,这里笔者删除掉了校验入参的一些非主干逻辑,我们可以看到代码也就十几行,非常简单,接下来让我们一行一行的去看一下代码

function compose (middleware) {
  // 返回一个匿名函数,next为可选参数
  return function (context, next) {
    // 记录当前执行位置的游标
    let index = -1
    // 从第一个中间件开始,串起所有中间件
    return dispatch(0)
    function dispatch (i) {
      // 为了不破坏洋葱圈模型,不允许在单个中间件中执行多次next函数
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      // 更新游标
      index = i
      let fn = middleware[i]
      // 判断边界,假如已经到到边界了,可执行外部传入的回调
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        // 核心处理逻辑,进入fn的执行上下文的时候,dispatch就是通过绑定下一个index,变成了next,进入到下一个中间件
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

compose接收一个的函数数组[fn1, fn2, fn3, ...],compose调用后,返回了一个匿名函数,匿名函数接收两个参数

  • 第一个参数是上下文,对于koa的上下文不在本文的探究范围,我们只要记得这个是在各个中间件中的共享的就可以了
  • 第二个参数标记为next,可选参数,在中间件执行的最后检查执行,这个和自定义的中间件中的next不完全一样,一般是初始化返回了匿名函数后,调用方自己指定,用于处理一下默认逻辑

通过源码可以看出,compose是提供了一个洋葱模型完全执行的回调,通过把函数存储起来,用next作为钥匙,串起了我们所有的中间件,其核心代码就是:

Promise.resolve(fn(context, dispatch.bind(null, i + 1)))

进入fn的执行上下文的时候,dispatch就是通过绑定下一个index,变成了next,进入到下一个中间件。另外fn(中间件函数)可以是个异步函数,Promise.resolve会等到内部异步函数resolve之后触发

单次调用限制

假如在单个中间件中执行多次next函数的话,会造成下游的中间件多次执行,这样就破坏了洋葱圈模型,因此限制了在单个中间件中只能执行一次next函数,实现方式时在函数记录了一个游标index,初始值是-1;这个游标会记录当前执行到哪个中间件,用来禁止在中间件中多次调用next函数

在一个中间件内多次调用next的时候,你就会收到下面这个报错

UnhandledPromiseRejectionWarning: Error: next() called multiple times

koa-compose与流程引擎

koa-compose不仅仅只是koa的一个依赖包,在有些场景下完全可以作为一个独立的工具来使用的,这里模拟一个代码检测工具的应用,完全可以作为一个流程引擎来使用

const koaCompose = require('koa-compose');
function download = (ctx, next) {
  console.log('download code');
  next();
}
function check = (ctx, next) {
  console.log('check style');
  next();
}
function post = (ctx, next) {
  next();
  console.log('post result', ctx.result);
}
function clean = (ctx, next) {
  next();
  console.log('clean temp, remove code');
}
const flowEngine = koaCompose([download, check, post, clean]);
flowEngine(ctx as Context);

上述可以看作一个基于koa-compose实现的流程引擎,在node中,我们会经常处理一些多阶段的任务,完全可以通过这样的方式来实现

总结

koa的洋葱圈模型在面试中经常会被问到,建议可以写一下、理解一下koa-compose的源码;另外koa-compose作为一个流程引擎也是一个很有用的工具,在有些场景下会有意想不到的效果。

以上就是20行代码简单实现koa洋葱圈模型示例详解的详细内容,更多关于koa洋葱圈模型的资料请关注脚本之家其它相关文章!

相关文章

  • 浅谈使用nodejs搭建web服务器的过程

    浅谈使用nodejs搭建web服务器的过程

    这篇文章主要介绍了浅谈使用nodejs搭建web服务器的过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Node.js系列之发起get/post请求(2)

    Node.js系列之发起get/post请求(2)

    这篇文章主要为大家详细介绍了Node.js系列之发起get/post请求,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • window10下node使用管理神器NVM安装配置超详细步骤

    window10下node使用管理神器NVM安装配置超详细步骤

    nvm全称Node Version Manager是 Nodejs 版本管理器,它让我们能方便的对 Nodejs 的版本进行切换,nvm 的官方版本只支持 Linux 和 Mac, Windows 用户,可以用 nvm-windows,这篇文章主要介绍了window10下node使用管理神器NVM安装配置超详细步骤,需要的朋友可以参考下
    2023-01-01
  • 使用nodejs搭建微信小程序支付接口的详细过程

    使用nodejs搭建微信小程序支付接口的详细过程

    前段时间做微信支付,遇到了很多坑,网上也没有讲解的特别明白的,通过借鉴各路人才的经验,最后也完成了,下面这篇文章主要给大家介绍了关于使用nodejs搭建微信小程序支付接口的详细过程,需要的朋友可以参考下
    2022-12-12
  • Electron架构深入探究

    Electron架构深入探究

    这篇文章主要为大家介绍了Electron架构深入探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • nodejs 图片预览和上传的示例代码

    nodejs 图片预览和上传的示例代码

    本篇文章主要介绍了nodejs 图片预览和上传的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • NodeJS开发人员常见五个错误理解

    NodeJS开发人员常见五个错误理解

    这篇文章主要介绍了NodeJS开发人员常见五个错误理解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • Nodejs 发布自己的npm包并制作成命令行工具的实例讲解

    Nodejs 发布自己的npm包并制作成命令行工具的实例讲解

    今天小编就为大家分享一篇Nodejs 发布自己的npm包并制作成命令行工具的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • 基于Node.js实现一键生成个性化二维码

    基于Node.js实现一键生成个性化二维码

    这篇文章主要为大家详细介绍了如何使用Node.js、Jimp和QRCode库,结合一个简单的脚本,通过命令行命令来快速给二维码加上指定的背景,打造更有个性化的二维码,感兴趣的可以了解下
    2024-03-03
  • 详解nodejs 文本操作模块-fs模块(五)

    详解nodejs 文本操作模块-fs模块(五)

    本篇文章主要介绍了nodejs 文本操作模块-fs模块(五),这里再说最后一个我看来很重要的方法,监听文件或者目录的的方法watchFile。有兴趣的可以了解一下。
    2016-12-12

最新评论