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

目录
  • 引言
  • koa中间件的使用
  • 洋葱圈模型
    • 洋葱圈模型的实现,koa-compose
  • 单次调用限制
  • koa-compose与流程引擎
  • 总结

引言

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洋葱圈模型的资料请关注我们其它相关文章!

(0)

相关推荐

  • Node.js 网络框架koa compose中间件使用解析

    目录 前言 koa-compose 洋葱模型 源码解析 总结 前言 学习目标: koa-compose 洋葱模型 源码地址:koajs/compose koa-compose Koa-compose 是一个 Koa 中间件工具,Koa 是一个流行的 Node.js 网络框架.Koa-compose 允许你将多个中间件函数组合成一个单独的函数,这样可以更容易地管理和重用中间件. 在 Koa 中,中间件函数是按照特定顺序调用的函数,用于处理传入的 HTTP 请求并生成响应.中间件函数可以执行各种任务

  • koa TS ESLint搭建服务器重构版过程详解

    目录 初始化项目目录 安装项目运行所需要的软件包 修改package.json 从.env中加载环境变量 配置路径别名 用法 目录规范 编码风格规范 Eslint 初始化项目目录 yarn init -y 安装项目运行所需要的软件包 生产依赖 yarn add koa koa-router cross-env module-alias dotenv koa:搭建 Koa 服务的核心软件包. koa-router:Koa 路由软件包. koa-bodyparser:解析 POST 请求参数的软件包

  • 前端使用koa实现大文件分片上传

    目录 引言 前端 拆分上传的文件流 后端 接收文件片段 合并文件片段 总结 引言 一个文件资源服务器,很多时候需要保存的不只是图片,文本之类的体积相对较小的文件,有时候,也会需要保存音视频之类的大文件.在上传这些大文件的时候,我们不可能一次性将这些文件数据全部发送,网络带宽很多时候不允许我们这么做,而且这样也极度浪费网络资源. 因此,对于这些大文件的上传,往往会考虑用到分片传输. 分片传输,顾名思义,也就是将文件拆分成若干个文件片段,然后一个片段一个片段的上传,服务器也一个片段一个片段的接收,最

  • 洋葱模型 koa-compose源码解析

    目录 洋葱模型 源码 动手 总结 洋葱模型 koa-compose是一个非常简单的函数,它接受一个中间件数组,返回一个函数,这个函数就是一个洋葱模型的核心. 源码地址:github.com/koajs/compo… 网上一搜一大把图,我就不贴图了,代码也不上,因为等会源码就是,这里只是介绍一下概念. 洋葱模型是一个非常简单的概念,它的核心是一个函数,这个函数接受一个函数数组,返回一个函数,这个函数就是洋葱模型的核心. 这个返回的函数就是聚合了所有中间件的函数,它的执行顺序是从外到内,从内到外.

  • Nodejs中koa2连接mysql的实现示例

    目录 将查询结果转为对象或者数组 mysql2的使用 Prepared Statement(预处理语句) Connection Pools(连接池) Promise方式 sequelize Sequelize的使用 Sequelize的单表操作 Sequelize的一对多操作 Sequelize的多对多操作 将查询结果转为对象或者数组 在真实开发中,实际上某些查询结果应该放入到一个对象中 JSON_OBJECT:()中是key-value的形式 SELECT products.id as id,

  • vue路由history模式页面刷新404解决方法Koa Express

    目录 为什页面刷新会出现404 Node服务使用Koa框架 Node服务使用Express框架 为什页面刷新会出现404 因为vue项目中路由hash模式改为了history模式,由于hash模式时url带的#号后面是哈希值不会作为url的一部分发送给服务器,而history模式下当刷新页面之后浏览器会直接去请求服务器,而服务器没有这个路由,于是就出现404. 那为什么页面跳转就是正常的?跳转时其实不是通过请求服务器的,而是通过js操作history的API改变地址完成的. 建议:非C端系统可以

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

    目录 引言 koa中间件的使用 洋葱圈模型 洋葱圈模型的实现,koa-compose 单次调用限制 koa-compose与流程引擎 总结 引言 koa想必很多人直接或间接的都用过,其源码不知道阅读本文的你有没有看过,相当精炼,本文想具体说说koa的中间件模型,一起看看koa-compose的源码,这也是koa系列的第一篇文章,后续会更新一下koa相关的其他知识点 koa中间件的使用 先让我们启动一个koa服务 // app.js const koa = require('koa'); cons

  • nodejs 实现简单的文件上传功能(示例详解)

    首先需要大家看一下目录结构,然后开始一点开始我们的小demo. 文件上传总计分为三种方式: 1.通过flash,activeX等第三方插件实现文件上传功能. 2.通过html的form标签实现文件上传功能,优点:浏览器兼容好. 3.通过xhr level2的异步请求,可以百度formData对象. 这里使用2做个练习. node插件请看下package.json文件 { "name": "upload", "version": "0.1

  • PHP基于关联数组20行代码搞定约瑟夫问题示例

    本文实例讲述了PHP基于关联数组20行代码搞定约瑟夫问题.分享给大家供大家参考,具体如下: 记得前段时间一写做java开发的兄弟对我说他java60行做了个约瑟夫问题,挺不错的.调侃php应该写这个挺不行的. 于是 呵呵... 洋洋洒洒 20行,写完自己都有些不相信了.哈哈 让不了解php的见识哈php的快捷轻便之处. ps:其实个人挺反感用代码行数来衡量代码数量的,感觉常把代码行数挂嘴边的大多无奈装2.此文仅属闲余娱乐. 回顾一下约瑟夫问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最

  • 不到20行代码用Python做一个智能聊天机器人

    伴随着自然语言技术和机器学习技术的发展,越来越多的有意思的自然语言小项目呈现在大家的眼前,聊天机器人就是其中最典型的应用,今天小编就带领大家用不到20行代码,运用两种方式搭建属于自己的聊天机器人. 1.神器wxpy库 首先,小编先向大家介绍一下本次运用到的python库,本次项目主要运用到的库有wxpy和chatterbot. wxpy是在 itchat库 的基础上,通过大量接口优化,让模块变得简单易用,并进行了功能上的扩展.什么是接口优化呢,简单来说就是用户直接调用函数,并输入几个参数,就可以

  • python随机生成大小写字母数字混合密码(仅20行代码)

    用简单的方法生成随机性较大的密码 仅用20行代码随机生成密码 核心思路:利用random模块 random模块随机生成数字,大小写字母,循环次数 while循环+随机生成的循环次数-->随机plus++ 大写字母ASKII码在65-90之间 小写字母Askll码在97-122之间 最终效果: x个大写字母+y个数字+z个小写字母(x,y,z均随机) 随机性相较于以往单调的 小写+数字+大写+小写+数字+大写- 循环有所提升 import random print("随机数生成") time

  • Python使用20行代码实现微信聊天机器人

    近来,打开微信群发消息,就会秒收到一些活跃分子的回复,有的时候感觉对方回答很在理,但是有的时候发现对方的回答其实是驴唇不对马嘴,仔细深究发现,原来对方是机器人.今天,小编就带大家用20行代码,带你一起打造一个微信聊天机器人,让你的微信群一直嗨不停~~ 首先我们需要安装一个微信相关的第三方库,itchat,在Windows上通过命令:pip install itchat,就可以将其安装. 其二,我们需要去图灵机器人官网:http://www.tuling123.com,注册一下,即可获得一个机器人

  • 女神相册密码忘记了 我只用Python写了20行代码

    视频地址 我用20行代码,帮女神破解相册密码 一.事情是这样的 今早上班,公司女神小姐姐说,她去年去三亚旅游的照片打不开了 好奇问了一下才知道. 原来是,她把照片压缩了,而且还加了密码. 但是密码不记得了,只记得是一串6位数字. 话说照片压缩率也不高,而且还加密,难道是有什么可爱的小照片 但是作为一个正(ba)直(gua)的技术人员 我跟她说:"这事交给我,python写个脚本,帮你破解掉~~" 二.首先回顾一下女神的操作流程 对相册进行压缩的时候,添加了密码. LIke This ↓

  • JavaScript用20行代码实现虎年春节倒计时

    春节将至,小梦相信大家跟小朦梦一样很激动呀.为了迎接虎年春节到来,小梦撸了一个虎年春节倒计时,仅20行代码用js就实现啦,是不是很简单呢?我们用这20行代码不仅能做个虎年春节倒计时,还能从中学到JS相关知识,一举两得!话不多说,接下来小伙伴们跟着小梦一起也动手试试吧! 虎年春节倒计时局部效果图 虎年春节倒计时全局效果图,灰常可爱的小老虎呀~ JS关键代码实现 window.onload=function starttime(){ // 2022年春节时间 time(h1,'2022/2/1');

  • 20行代码教你用python给证件照换底色的方法示例

    1.图片来源 该图片来源于百度图片,如果侵权,请联系我删除!图片仅用于知识交流. 2.读取图片并显示 imread():读取图片: imshow():展示图片: waitkey():设置窗口等待,如果不设置,窗口会一闪而过: import cv2 import numpy as np # 读取照片 img=cv2.imread('girl.jpg') # 显示图像 cv2.imshow('img',img) # 窗口等待的命令,0表示无限等待 cv2.waitKey(0) 效果如下: 3.图片缩

  • Java用20行代码实现抖音小视频批量转换为gif动态图

    本文主要介绍了Java用20行代码实现抖音小视频批量转换为gif动态图,分享给大家,具体如下: 效果图 本功能实现需要用到第三方jar包 jave,JAVE 是java调用FFmpeg的封装工具. spring boot项目pom文件中添加以下依赖 <!-- https://mvnrepository.com/artifact/ws.schild/jave-core --> <dependency> <groupId>ws.schild</groupId>

随机推荐