浅谈Koa和Express的区别

 更新时间:2026年06月20日 09:11:27   作者:MariaH  
本文主要介绍了浅谈Koa和Express的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前置核心概念

  1. Express:线性瀑布式中间件模型,基于回调,无原生 async/await 支持;
  2. Koa2:洋葱圈中间件模型,基于 async/await + compose,原生支持异步;
  3. 同步:代码无定时器、IO、数据库、Promise;
  4. 异步:定时器、文件读写、数据库、网络请求、Promise、async 函数。

统一测试需求:
3 层中间件,要求:

  • 中间件1:前置拼接 a,后置输出最终结果
  • 中间件2:拼接 b
  • 中间件3:拼接 c
    最终在中间件1后置打印 message = abc

一、同步场景:Express vs Koa(无异步操作)

1. Express 同步中间件

const express = require('express')
const app = express()

app.use((req, res, next) => {
  req.msg = 'a'
  console.log('Express 中间件1 前置')
  next()
  // next之后的代码,所有下游中间件执行完才执行
  console.log('Express 中间件1 后置:', req.msg)
})

app.use((req, res, next) => {
  req.msg += 'b'
  console.log('Express 中间件2 前置')
  next()
  console.log('Express 中间件2 后置')
})

app.use((req, res) => {
  req.msg += 'c'
  console.log('Express 中间件3 执行完毕')
  res.send(req.msg)
})

app.listen(3000)

执行顺序(同步无任何异步)

Express 中间件1 前置
Express 中间件2 前置
Express 中间件3 执行完毕
Express 中间件2 后置
Express 中间件1 后置: abc

同步下 Express 特点

  1. 同步代码下,执行流程和Koa洋葱模型表现一致
  2. next() 是同步跳转,会立刻执行下一个中间件;
  3. next() 后面代码会等所有下游中间件全部走完再回头执行;
  4. 同步场景二者行为几乎无差异。

2. Koa 同步中间件

const Koa = require('koa')
const app = new Koa()

app.use((ctx, next) => {
  ctx.msg = 'a'
  console.log('Koa 中间件1 前置')
  next()
  console.log('Koa 中间件1 后置:', ctx.msg)
})

app.use((ctx, next) => {
  ctx.msg += 'b'
  console.log('Koa 中间件2 前置')
  next()
  console.log('Koa 中间件2 后置')
})

app.use(ctx => {
  ctx.msg += 'c'
  console.log('Koa 中间件3 执行完毕')
  ctx.body = ctx.msg
})

app.listen(8000)

输出顺序和Express完全一致

Koa 中间件1 前置
Koa 中间件2 前置
Koa 中间件3 执行完毕
Koa 中间件2 后置
Koa 中间件1 后置: abc

同步场景小结

同步代码时,Express 和 Koa 表现完全相同
自上而下执行 next() 前代码,全部走完后,自下而上执行 next() 后代码。
差异只在异步场景爆发。

二、异步场景:Express(巨大缺陷)vs Koa(完美可控)

场景设定:中间件2中加入异步延时操作(模拟数据库/接口请求)

// 模拟异步IO
function sleep(time) {
  return new Promise(resolve => setTimeout(resolve, time))
}

1. Express 异步中间件(重大问题)

const express = require('express')
const app = express()

app.use((req, res, next) => {
  req.msg = 'a'
  console.log('Express 中间件1 前置')
  next()
  // 异步导致此处提前执行,拿不到完整abc
  console.log('Express 中间件1 后置:', req.msg)
})

app.use(async (req, res, next) => {
  req.msg += 'b'
  console.log('Express 中间件2 前置')
  // 异步延时2秒
  await sleep(2000)
  next()
  console.log('Express 中间件2 后置')
})

app.use((req, res) => {
  req.msg += 'c'
  console.log('Express 中间件3 执行完毕')
  res.send(req.msg)
})

app.listen(3000)

执行输出顺序(错误)

Express 中间件1 前置
Express 中间件2 前置
Express 中间件1 后置: a  // 重点:异步阻塞前直接回头执行后置!
// 等待2秒后
Express 中间件3 执行完毕
Express 中间件2 后置

问题分析

  1. Express 不识别 async/awaitnext() 调用后直接同步返回,不会等待异步代码完成;
  2. 中间件2内部 await sleep 阻塞时,事件循环让出,Express 直接回到中间件1执行 next() 后面的代码;
  3. 中间件1后置代码提前执行,req.msg 只有 a,拿不到最终拼接结果;
  4. 无法实现“等所有下游逻辑完成再执行后置”的需求。

Express 异步解决方案(丑陋)

必须手动在异步结束后调用 next(),强行嵌套回调,回调地狱:

app.use((req, res, next) => {
  req.msg += 'b'
  console.log('中间件2 前置')
  setTimeout(() => {
    req.msg += '延迟b'
    next() // 异步完成后再放行
  }, 2000)
})

缺点:多层异步会无限嵌套,流程不可读、难以维护。

2. Koa 异步中间件(原生支持,流程不乱)

const Koa = require('koa')
const app = new Koa()

function sleep(time) {
  return new Promise(resolve => setTimeout(resolve, time))
}

app.use(async (ctx, next) => {
  ctx.msg = 'a'
  console.log('Koa 中间件1 前置')
  await next() // 等待所有下游中间件全部执行完成才往下走
  console.log('Koa 中间件1 后置:', ctx.msg)
})

app.use(async (ctx, next) => {
  ctx.msg += 'b'
  console.log('Koa 中间件2 前置')
  await sleep(2000) // 异步阻塞,不会跳出当前中间件
  await next()
  console.log('Koa 中间件2 后置')
})

app.use(ctx => {
  ctx.msg += 'c'
  console.log('Koa 中间件3 执行完毕')
  ctx.body = ctx.msg
})

app.listen(8000)

正确输出顺序

Koa 中间件1 前置
Koa 中间件2 前置
// 等待2秒
Koa 中间件3 执行完毕
Koa 中间件2 后置
Koa 中间件1 后置: abc

Koa 异步核心原理

  1. Koa 使用 compose 组合中间件,所有中间件被包装成 Promise 调用链;
  2. await next() 会暂停当前中间件,完整等待下游所有中间件(含异步)全部执行完毕,才恢复当前中间件剩余代码;
  3. 无论中间件内部有多少层异步、定时器、数据库操作,洋葱模型执行顺序永远稳定不变;
  4. 无回调嵌套,线性代码书写复杂异步流程。

三、同步/异步场景完整异同对照表

1. 相同点

同步代码场景

  • 两者执行顺序完全一致:自上而下前置 → 自下而上后置;
  • next() 同步跳转,下游全部执行完再回头执行后置逻辑;
  • 简单同步接口(无数据库/文件)表现无区别。

基础能力一致

  • 都基于 Node http 模块封装;
  • 都依靠中间件处理请求;
  • 都支持多层中间件堆叠。

2. 不同点(分同步、异步区分)

维度ExpressKoa2
同步执行逻辑线性流转,next后代码后置执行洋葱流转,和Express同步表现一致
异步执行逻辑next() 同步返回,不会等待异步;异步阻塞会直接跳出当前中间件,后置代码提前执行,流程错乱await next() 阻塞等待全部下游异步完成,洋葱顺序永久稳定
异步语法支持原生不支持async/await,需回调嵌套,极易产生回调地狱原生基于Promise+async/await,无嵌套
中间件底层实现数组顺序遍历,线性瀑布模型compose递归调用,Promise链式洋葱模型
上下文对象req、res分离,全局挂载数据容易冲突统一ctx上下文,一次请求独立ctx,数据隔离
异步错误捕获异步内部抛出错误无法被全局错误捕获,必须手动try/catch传err给next(err)全局可捕获async中间件抛出的错误,统一error事件处理
多异步串联多层异步必须嵌套,可读性极差平铺书写,await顺序执行,逻辑清晰

四、异步错误处理对比(补充关键差异)

Express 异步错误捕获缺陷

app.use(async (req, res, next) => {
  // 异步抛出错误,Express 捕获不到,直接崩溃
  throw new Error('数据库查询失败')
  next()
})

解决方式:必须手动 try/catch + next(err)

app.use(async (req, res, next) => {
  try {
    await sleep(1000)
    throw new Error('异常')
  } catch (err) {
    next(err) // 手动传递错误
  }
})
// 错误中间件接收
app.use((err, req, res, next) => {
  res.status(500).send(err.message)
})

Koa 异步错误天然支持

app.use(async (ctx, next) => {
  await sleep(1000)
  throw new Error('数据库异常')
})
// 全局统一捕获,无需手动传递
app.on('error', (err, ctx) => {
  ctx.status = 500
  ctx.body = { msg: err.message }
})

所有 async 中间件抛出的异常都会被 Koa 内部 Promise 捕获,自动触发全局 error 事件。

五、总结核心结论

只写同步代码:Express 和 Koa 几乎没有区别,洋葱/瀑布执行效果一样;

项目存在大量异步(数据库、Redis、文件、接口) :二者出现本质鸿沟:

  • Express:异步会打乱中间件执行顺序,回调嵌套、错误处理繁琐;
  • Koa:依靠 await next() 固定洋葱执行流,异步代码平铺书写,错误统一捕获;

核心根源:
Express 中间件只是普通回调函数,无 Promise 封装;
Koa 通过 compose 将所有中间件包装成 Promise 调用链,让异步流程可控。

到此这篇关于浅谈Koa和Express的区别的文章就介绍到这了,更多相关Koa和Express区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • async/await优雅的错误处理方法总结

    async/await优雅的错误处理方法总结

    这篇文章主要给大家介绍了关于async/await优雅的错误处理方法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-01-01
  • node中的cookie的具体使用

    node中的cookie的具体使用

    这篇文章主要介绍了node中的cookie的具体使用,详细的介绍了什么是cookie和cookie的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • 使用命令行升级Node.js的版本的操作指南

    使用命令行升级Node.js的版本的操作指南

    这篇文章主要给大家介绍了关于如何使用命令行升级Node.js的版本的操作指南,文中介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧
    2023-11-11
  • 详解用node-images 打造简易图片服务器

    详解用node-images 打造简易图片服务器

    本篇文章主要介绍了详解用node-images 打造简易图片服务器,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • 利用Chrome DevTools直接调试Node.js和JavaScript的方法详解(并行)

    利用Chrome DevTools直接调试Node.js和JavaScript的方法详解(并行)

    现在我们可以用浏览器调试node.js了!!!下面这篇文章主要介绍了利用Chrome DevTools直接调试Node.js和JavaScript的方法步骤,文中介绍的很详细,需要的朋友可以参考学习,下面来一起看看吧。
    2017-02-02
  • Node.js使用对话框ngDialog的示例代码

    Node.js使用对话框ngDialog的示例代码

    本篇文章主要介绍了Node.js使用对话框ngDialog的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • 在linux中使用包管理器安装node.js

    在linux中使用包管理器安装node.js

    这篇文章主要介绍了在linux中使用包管理器安装node.js的方法以及具体安装过程,非常详细,推荐给大家,有需要的小伙伴参考下吧。
    2015-03-03
  • node.js中的fs.fsync方法使用说明

    node.js中的fs.fsync方法使用说明

    这篇文章主要介绍了node.js中的fs.fsync方法使用说明,本文介绍了fs.fsync的方法说明、语法、接收参数、使用实例和实现源码,需要的朋友可以参考下
    2014-12-12
  • 如何在Node和浏览器控制台中打印彩色文字

    如何在Node和浏览器控制台中打印彩色文字

    这篇文章主要介绍了如何在Node和浏览器控制台中打印彩色文字,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • express搭建的nodejs项目使用webpack进行压缩打包

    express搭建的nodejs项目使用webpack进行压缩打包

    对于打包这个问题它并不是难点,但是对于我们这种初学者来说,根本就不知道应该怎么做,下面这篇文章主要给大家介绍了关于express搭建的nodejs项目使用webpack进行压缩打包的相关资料,需要的朋友可以参考下
    2022-12-12

最新评论