Koa 洋葱模型

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

Koa 洋葱模型

提到 Node.js,就不得不提目前炙手可热的两大框架 Express 和 Koa,他俩都是 NodeJs 的主流开发框架。

Express 和 Koa:

Express是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。
Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。

就现在的版本,koaexpress最大的区别也只有两点,这也是koa的最大的两个优点:

  1. koaexpress轻量的多,koa更像是一个中间件框架,只是一个基础的架子,需要用到的相应的功能时,用相应的中间件来实现就好,比如路由系统、静态资源托管等等。
  2. 对于 next() 的封装, koa 是返回 promise, express 是 void。所以koa的 next() 可以用 await 修饰,被 await 修饰的 next() 会等待上一个中间件执行完毕后再执行自身的 next()。
异步处理:

express是基于回调来处理,至于回调到底有多么的不好用?

// 原始使用回调版本
app.use((req, res, next) => {
  console.log('start');
  next();
  // 在这里,读取文件1,2的内容,并合并写入到3中
  fs.readFile('./file1.txt', (err1, data1) => {
    if (err) {
      console.log('failed in file1')
    }
    console.log(data1.toString());
    fs.readFile('./file2.txt', (err2, data) => {
      if (err2) {
        console.log('failed in file2')
      }
      console.log(data2.toString());
      fs.writeFile('./file3.txt', data1 + data2, err3 => {
        if (err3) {
          console.log('failed in file3')
        }
        console.log('done');
      });
    });
  });
});

Koa NoCallback! No Callback!No Callback!重要的事情说三遍!

Koa1 和 Koa2:

koa1基于co库,所以koa1利用Generator来代替回调,而koa2由于node7.6.0对async/await的支持,所以koa2利用的是async/await。
Koa1Koa2中间件的实现思路是一样的,只是实现方式有所区别。koa1 的中间件使用 generator 函数,使用 yield next 进入下一个中间件,koa2 中间件使用 async 函数,通过await next()进入下一个中间件。

中间件模型:

中间件

首先,中间件并不是 express/koa 独有的概念,所以广泛意义的中间件的意义是:
中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。
将上面的话翻译成人话就是:
在一次消费过程(在 koa 中可以看成是一次接受请求到响应结束的过程)中被调用的函数,就属于中间件。

中间件函数能够访问请求对象 (req)、响应对象(res) 以及应用程序的请求/响应循环中的下一个中间件函数,下一个中间件函数通常由名为 next 的变量来表示。
在这里插入图片描述
想象一下当业务逻辑复杂的时候,为了明确和便于维护,需要把处理的事情分一下,分配成几个部分来做,而每个部分就是一个中间件。

洋葱圈模型

Koa 的中间件模型就是洋葱圈模型。Koa的每个中间件就像是一个洋葱圈,每次当有一个请求进入的时候,每个中间件都会被执行两次。
在这里插入图片描述
在这里插入图片描述
每次执行下一个中间件传入两个参数 ctx 和 next,参数 ctx 是由 koa传入的封装了 request 和 response 的变量,可以通过它访问 request 和 response,next 就是下一个要进入执行的中间件。
其实很明显,在koa的中间件中,通过 next 函数,将中间件分成了两部分,next 上面的一部分会首先执行,而下面的一部分则会在所有后续的中间件调用之后执行。当一个中间件独调用 next() 则该函数暂停,并将控制传递给定义的下一个中间件。当在下游没有更多的中间件执行后,堆栈将展开并且每个中间件将恢复执行其上游行为。
当我们使用koa进行开发的时候,因为读取数据库或是http请求等都是异步请求,所以我们为了保证洋葱模型会使用号称异步终极解决方案的async/await。

const app = new Koa()
// 中间件A
app.use(async (ctx, next) => {
    console.log("A1")
    await next()
    console.log("A2")
});
// 中间件B
app.use(async (ctx, next) => {
    console.log("B1")
    await next()
    console.log("B2")
});
// 中间件C
app.use(async (ctx, next) => {
    console.log("C1")
    await next()
    console.log("C2")
});

app.listen(3000);
// 输出
// A1 -> B1 -> C1 -> C2 -> B2 -> A2

当程序运行到await next()的时候就会暂停当前中间件,进入下一个中间件,处理完之后才会再回过头来继续处理。也就是说,当一个请求进入,中间件1会被第一个和最后一个经过,中间件2则是被第二和倒数第二个经过,依次类推。
例如,我们经常需要计算一个请求的响应时间,在 Koa 中, 我们可以在中间件的开始记录初始时间,当响应返回时,代码执行又回到了原来的中间件,此时根据当前时间和初始时间的时间差便得到了响应时间。

function responseTime() {
  return async function responseTime(ctx, next) {
    const start = Date.now()
    await next() // wait for other middleware to run
    const delta = Math.ceil(Date.now() - start)
    ctx.set('X-Response-Time', delta + 'ms')
  })
}}
一个栗子

主要用来对比 koa 和 express 的执行顺序

let A = '';
app.use(async (ctx, next) => {
  A = await new Promise((resolve, reject) => {
    fs.readFile('./a.txt', function(err1, data1) {
      resolve(data1.toString());
    });
  });
  console.log('1');
  await next();
  console.log('1 call');
});

app.use(async (ctx, next) => {
  console.log('2');
  await next();
  console.log('2 call');
});

app.use(async (ctx, next) => {
  await new Promise((resolve, reject) => {
    fs.writeFile('./b.txt', A, function(err) {
      if(err) {
        reject(err);
      }
      resolve();
    });
  });
  console.log('3');
  next();
  const B = await new Promise((resolve, reject) => {
    fs.readFile('./b.txt', function(err, data) {
      if(err) {
        reject(err);
      }
      resolve(data.toString());
    });
  });
  console.log('3 call ',B);
});
// 1 => 2 => 3 => 3 call => 2 call => 1 call
// 1 => 2 => 2 call => 1 call => 3 => 3 call

洋葱圈核心实现

每个中间件都接收了一个next参数,在 next 函数运行之前的中间件代码会在一开始就执行,next函数之后的代码会在内部的中间件全部运行结束之后才执行。
要想达到上面洋葱圈的运行效果,我们需要做什么呢?

  1. 首先我们要知道当前中间件的数组集合
  2. 然后构建一个组合方法,对这些中间件按照洋葱的结构进行组合,并执行

我们带着这样的一个思路,再回头来看Koa是如何实现的:
3. this.middleware是中间件集合的数组
4. koa-compose模块的 compose 方法用来构建执行顺序

完美!下面只需要具体分析一下它们分别做了什么就可以了

// middleware用来保存中间件
app.use = (fn) => {
  this.middleware.push(fn)
  return this
}
// compose组合函数来规定执行次序
function compose (middleware) {
  // context:上下文,next:传入的接下来要运行的函数
  return function (context, next) {
    function dispatch (i) {
      // 中间件
      let fn = middleware[i]
      if (!fn) return Promise.resolve()
      try {
        // 我们这边假设和上文中的例子一样,有A、B、C三个中间件
        // 通过dispatch(0)发起了第一个中间件A的执行
        // A中间件执行之后,next作为dispatch(1)会被执行
        // 从而发起了下一个中间件B的执行,然后是中间件C被执行
        // 所有的中间件都执行了一遍后,执行Promise.resolve()
        // 最里面的中间件C的await next()运行结束,会继续执行console.log("C2")
        // 整个中间件C的运行结束又触发了Promise.resolve
        // 中间件B开始执行console.log("B2")
        // 同理,中间件A执行console.log("A2")
        return Promise.resolve(fn(context, () => {
          return dispatch(i + 1)
        }))
      } catch (err) {
        return Promise.reject(err)
      }
    }
    return dispatch(0)
  }
}

Koa 利用了在中间件中间传入 next 参数的方法,再结合 middleware 中间件数组和 compose 组合函数,构建了洋葱圈的中间件执行结构。

完!

【Node】express vs koa 洋葱模型 先看一张图:这张图在网上特别流行,基本搜索一下洋葱模型每篇文章都有这张图来做讲解。比较形象的解释了洋葱模型是在处理请求来和响应请求之间的问题。可以类比栈,先进后出。在洋葱模型中,每一层相当于一个中间件,用来处理特定的功能,比如错误处理、Session处理等等。其处理顺序先是next()前请求(Request,从外层到内层)然后执行next()函数,最后是next()后响应(Response,从内层到外层),也就是说每一个中间件都有两次处理时机。 阅读详情

相关推荐

koa - 洋葱模型浅析

,它的核心工作包括下面两个方面:如下图: 如下是洋葱代码的案例: 4.源码解析 use方法 listen方法 compose方法 compose方法是洋葱模型的核心,compose方法中有一个dispatch方法,第一次调用的时候,执行的是第一个中间件函数,中间件函数执行的时候就是再次调用dispatch函数,也就说形成了一个递归,这就是next函数执行的时候会执行下一个中间件的原因,因此形成了一个洋葱模型。 5.为什么需要洋葱模型? 因为很多时候,在一个app里面有很多中间件,,用葱模型

weixin_38245947的博客 1217

Koa洋葱模型到底是什么?

什么是KoaKoa是一个精简的node框架,被认为是第二代Node框架,其最大的特点就是独特的中间件流程控制,是一个典型的洋葱模型,它的核心工作包括下面两个方面: 将node原生的req和res封装成为一个context对象。 基于async/await的中间件洋葱模型机制。 Koa1和Koa2在源码上的区别有何不同? Koa1是使用generator、yield的模式。 Koa2使用的是async/await + Promise的模式。 什么是洋葱模型Koa洋葱模型是以next()

Always-Learning 4773

别再死记硬背了!用这3个真实场景,彻底搞懂Koa中间件洋葱模型

本文通过三个实战案例深入解析Koa中间件洋葱模型,包括日志记录、权限校验和请求调试等场景,帮助开发者掌握Node.js中Koa中间件的核心机制。文章详细展示了如何利用洋葱模型的特性优化API开发流程,提升代码可维护性和性能。

weixin_42549451的博客 311

Koa洋葱模型

概览: Koa洋葱模型指的是以next()函数为分割点,先由外到内执行Request的逻辑,再由内到外执行Response的逻辑。通过洋葱模型,将多个中间件之间通信等变得更加可行和简单。其实现的原理并不是很复杂,主要是compose方法。 什么是洋葱模型 先来看一个demo const Koa = require('koa'); const app = new Koa(...

ygunoil的博客 436

Koa核心—洋葱模型

洋葱模型 koa框架的业务流程是一个完全的异步编程模型,通过ctx上下文对象来贯穿http的上下游。那么我们最重要的就是理解洋葱模型。 const koa = require("Koa") const app = new koa() app.use((ctx,next)=>{ console.log(1) next() console.lo...

weixin_44287796的博客 1757

koa洋葱模型

koa-compose:koa-compose则是将 koa/koa-router 各个中间件合并执行,结合 next() 形成一种串行机制,并且是支持异步,这样就形成了洋葱模型 图解:一个洋葱来一刀![在这里插入图片描述](https://img-blog.csdnimg.cn/201907100849543.jpg?x-oss-process=image/watermark,type_ZmF...

2578

聊一聊KOA洋葱模型

Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。 其实KOA是什么,相比不用我多说,很多前端的小伙伴都了解...

willemwei023的博客 3200

koa中间件洋葱模型

何为中间件?别被这个词吓到了! 中间件本质是函数,在koa实例中使用use方法,把函数作为参数传入,这个函数便可叫做中间件 如: const Koa = require("koa") const app = new Koa(); function sayHello() { console.log("hello world"); } app.use(sayHello) app.listen(...

m0_37922914的博客 868

koa2 洋葱模型

示意图 koa2 是基于 async 和 await 实现的,使用 next 进行流程控制。这样就可以使用 await next() 进行一种特殊的执行顺序,等下一个路由执行完毕之后,再执行当前路由,通常称为 “洋葱模型”。 何谓 “洋葱模型” 就像那首歌一样,“一层一层剥开我的心”,koa2 中的 “洋葱模型” 也是这样,匹配到的路由,可以一层一层进去,进入最内层之后,再一层一层出来。 例如这个 demo: api.get("/", async function (ctx, next) { cons

缚己 775

你需要掌握的 Koa 洋葱模型中间件

在这之前,我们先简单看看 Koa 是如何使用的。在 Koa 中,我们通过 app.use 方法注册中间件中间件可以注册多个,它们的执行顺序和注册时机相关,先注册的先执行。所谓中间件就是一个函数,这个函数接受 Koa 提供的两个参数:ctx 上下文对象;next 函数。ctx 上有各种参数,比如请求对象 request 和响应对象 response。调用 next 函数会执行下一个的中间件,如果你不调用 next 函数,那下一个中间件就不会执行。// 中间件 1:记录请求花费时间。

fe_watermelon的博客 1068

Koa】为什么一定要保证是洋葱模型呢?

为什么一定要保证是洋葱模型呢? const Koa = require('koa'); const app = new Koa(); /* 聊一聊为什么我们通常需要在koa中间件加上async,如果这个中间件有next的话,我们还需要在next()的前面加上await?什么原因呢? 结论:如果不加上async和await,那么koa中间件的执行顺序就不一定按照洋葱模型的顺序来执行 例子: app.use((ctx,next) => { console.

Milk~每天分享一点点,技术进步一点点 961

使用异步编程保证Koa洋葱模型

koa框架的业务流程是一个完全的异步编程模型,通过ctx上下文对象来贯穿http的上下游。对我们来说最重要的就是理解洋葱模型

753

说说你对koa洋葱模型的理解?

用过Koa的,肯定对 Middleware(中间件) 有所了解,那我们就用中间件从现象出发,理解洋葱模型先自定义两个中间件:logTime:打印时间戳module.exports=function() { returnasyncfunction(ctx, next) { console.log("next前,打印时间戳:", newDate().getTime()) awaitnext() console.log("next后,打印时间戳:", newDate

hyqhyqhyqq的博客 278

Koa2】洋葱模型的理解

实践出真知之Koa洋葱模型

Milk~每天分享一点点,技术进步一点点 1360

洋葱模型php,koa 洋葱模型

分析1、首先这是koa2最简单的入门例子,我将通过这个入门例子来演示koa2的洋葱模型const Koa = require('koa');const app = new Koa();app.use((ctx,next)=>{console.log("第一个中间件执行");next() ;});// 第二个中间件app.use((ctx,next)=>{console.log("第二个...

weixin_29230773的博客 262

koa中间件梳理(洋葱模型

koa介绍 koa是一个精简的node框架,它主要做了以下事情: 基于node原生req和res为request和response对象赋能,并基于它们封装成一个context对象。 基于async/await(generator)的中间件洋葱模型机制。 koa1和koa2在源码上的区别主要是于对异步中间件的支持方式的不同。 koa1是使用generator、yield)的模式。 koa2使用...

nodeDocker的博客 1563

三言两语说透koa洋葱模型

Compose函数可以将需要顺序执行的多个函数复合起来,后一个函数将前一个函数的执行结果作为参数。这里利用了函数递归的机制。dispatch函数接收当前中间件的索引i,如果i大于中间件数组长度,则执行next函数。compose接收一个中间件数组作为参数,执行数组中的中间件,返回一个可以执行所有中间件的函数。所有中间件的回调函数中,执行中间件函数的时候,递归调用dispatch,同时将索引+1,表示执行下一个中间件。每个中间件都可以捕获自己的错误,并且不会影响其他中间件的执行,这样对错误处理更加友好。

qq_36789311的博客 1159
上一篇: 字节前端面经
echomsy
博客等级 码龄7年 22粉丝 18原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值