60行代码写一个超简陋的koa

一个很简陋的koa代码,可以很轻松的了解koa的工作流程

app.js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
const http = require('http')
const { Stream } = require('stream')
const qs = require('querystring')

const app = {
middleware: [],
use(fn) {
this.middleware.push(fn)
},
listen(port = 3000) {
const server = http.createServer(this.callback.bind(this))
return server.listen(port)
},
async callback(req, res) {
console.log('start')
const fn = compose(this.middleware)
// 这里有很多代理的方法
const ctx = {
request: req,
response: res,
req,
res,
get query() {
return { ...qs.parse(req.url.split('?')[1] || '') }
},
}
await fn(ctx)
.then(() => {
const { body } = ctx
if (!body) return res.end('null')
if (Buffer.isBuffer(body)) return res.end(body)
if (typeof body === 'string') return res.end(body)
if (body instanceof Stream) return body.pipe(res)
return res.end(JSON.stringify(body))
})
.catch((error) => {
res.end(error.toString())
})
console.log('end')
},
}

function compose(middleware = []) {
let index = -1

return (ctx, next) => {
return dispatch(0)
function dispatch(i) {
if (i <= index) return Promise.reject(new Error('next() called multile times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
} catch (err) {
return Promise.reject(err)
}
}
}
}

module.exports = app

test.js 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// const fs = require('fs')
const app = require('./app')

app.use(async function test1(ctx, next) {
console.log(1)
console.log('query', ctx.query)
await next()
console.log(6)
})
app.use(async function test2(ctx, next) {
console.log(2)
await next()
console.log(5)
})
app.use(async function test3(ctx, next) {
await next()
// ctx.body = fs.createReadStream('asset/a.jpg')
console.log(4)
})

app.listen(8000)

console.log('http://localhost:8000')