再談express與koa的對比

之前其實寫過一篇express和koa的對比, 可是後來發現裏面有很多謬誤. 因此一直惦記着糾正一下以前的錯誤, 尤爲關於中間件部分的對比.數據庫

這裏的express就拿更加簡單的connect代替express

connect的執行流程

一般咱們都說connect的中間件模型是線性的, 也就是一個一個往下執行的, 以下圖:app

image

這麼說固然是沒錯的, 可是當咱們執行下面代碼的時候可能會有那麼一點小小的困惑:koa

const connect = require('connect')

const app = connect()

app.use(function m1 (req, res, next) {
  console.log('m1')
  next()
  console.log('m1 end')
})

app.use(function m2 (req, res, next) {
  console.log('m2')
  next()
  console.log('m2 end')
})

app.use(function m3 (req, res, next) {
  console.log('m3')
  res.end('hello')
})

app.listen(8080)

當咱們訪問http://127.0.0.1:8080的時候, 控制檯會打印以下:異步

m1
m2
m3
m2 end
m1 end

這麼個結果跟咱們上面的模型彷佛有點出入, 不是說線性的嗎, 爲何next後面的代碼還會繼續執行? 固然這個咱們再以前已經有過結論了, 有興趣的能夠詳細瞧瞧, 咱們如今直接拿來結果, connect的中間件模型僞代碼表示以下:async

http.createServer(function (req, res) {
  m1 (req, res) {
    m2 (req, res) {
      m3 (req, res) {}
    }
  }
})

能夠看到就是一層一層嵌套的回調, 那麼再把咱們以前有點疑問的代碼簡化一下:函數

http.createServer(function (req, res) {
  console.log('m1')
  m1 (req, res) {
    console.log('m2')
    m2 (req, res) {
      m3 (req, res) {
        console.log('m3')
        res.end('hello')
      }
    }
    console.log('m2 end')
  }
  console.log('m1 end')
})

千萬別被上面的回調繞暈了, 就是很簡單的回調函數, 一切都解釋的通了: 即便res.end以後, 咱們的代碼仍是要繼續往下走的, 能夠這麼說connect的中間件其實也是洋蔥形的, 可是由於做爲同步代碼, 通常不回這麼作罷了, 那麼上面咱們能夠重現描述一下connect的中間件模型了:源碼分析

image

Koa的執行流程

一樣咱們再Koa源碼分析, 也是說過Koa的中間件模型: 洋蔥形
imagepost

如下面代碼爲例:ui

const Koa = require('koa')

const app = new Koa()

app.use(async function m1 (ctx, next) {
  console.log('m1')
  await next()
  console.log('m1 end')
})

app.use(async function m2 (ctx, next) {
  console.log('m2')
  await next()
  console.log('m2 end')
})

app.use(async function m3 (ctx) {
  console.log('m3')
  ctx.body = 'hello'
})

app.listen(8080)

訪問服務, 輸出:

m1
m2
m3
m2 end
m1 end

emm 貌似跟connect沒差異, 以前看過一篇文章, 實驗到這裏獲得了一個koa和express的中間件模型沒差異的結論, 包括我也是很迷惑, 固然是有差異的, 結論後面講. 一樣這裏直接拿出koa中間件的簡化模型:

Promise.resolve(async m1 () {
  console.log(m1)
  await Promise.resolve(async m2 () {
    console.log(m2)
    await Promise.resolve(async m3 () {
      console.log(m3)
      ctx.body = 'xxx'
     })
     console.log(m2 end)
  })
  console.log(m1 end)
})

咱們知道async/await的做用是'同步化'異步操做(看上去如此, 其實不是, 可是咱們不須要去管), 那這裏的Promise理所固然的被'同步'了, 也就是說console.log(m3 end)的一切異步操做均可以'同步化'.

結論

說出結論以前咱們其實能夠想一下, 既然connect的中間件也是洋蔥形的, 那麼跟koa同樣的用法彷佛也沒啥毛病, 那麼我來設想一下, 咱們的服務須要取數據庫裏的的一個用戶假設是getUser吧, getUser固然是異步的. 分別來看看connect和koa的作法吧:

// connect

app.use(function (req, res) {
  getUser(user => res.end(user))
})


// Koa
app.use(async (ctx) => {
  const user = await getUser()
  ctx.body = user
})

固然這麼看彷佛沒啥差異. 那直接給出結論吧(憋): connect的中間件是同步, 不會'等'其餘異步操做, koa則能夠'等'異步操做. 固然你不等也沒啥問題.

有啥問題能夠互相交流哈.

image

相關文章
相關標籤/搜索