koa vs express
总结:
Express 封装、内置了很多中间件,比如 connect和 router,而 KOA 则比较轻量,开发者可以根据自身需求定制框架;
Express 是基于 callback 来处理中间件的,而 KOA 则是基于 await/async;
在异步执行中间件时,Express 并非严格按照洋葱模型执行中间件,而 KOA 则是严格遵循的。
Express 使用 callback捕获异常,对于深层次的异常捕获不了,Koa 使用 try catch,能更好地解决异常捕获。
express demo
const express = require('express')
const path = require('path')
const fs = require('fs')
const app = express()
function sleep(time) {
return new Promise(resolve => {
setTimeout(resolve, time);
})
}
app.use(async function(req, res, next) {
console.log('middleware1 start', new Date().getTime());
// await sleep(1000).then(next);
next()
console.log('middleware1 end', new Date().getTime());
})
app.use(async function(req, res, next) {
console.log('middleware2 start', new Date().getTime());
// await sleep(2000).then(next);
next()
console.log('middleware2 end', new Date().getTime());
})
app.get('/', (req, res, next) => {
res.send('Hello express ')
})
app.listen(7777, () => {
console.log('7777 server on...');
})
/**
*
* async await
middleware1 start 1639029830146
middleware2 start 1639029831154
middleware1 end 1639029831154
middleware2 end 1639029833165
*/
/**
*
middleware1 start 1639029881890
middleware2 start 1639029881891
middleware2 end 1639029881898
middleware1 end 1639029881898
*/
koa demo
const Koa = require('koa')
const KoaRouter = require('koa-router')
const app = new Koa();
const router = new KoaRouter();
function sleep(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
app
.use(router.routes())
.use(router.allowedMethods())
router.use(async function (ctx, next) {
console.log('middleware1 start', new Date().getTime());
// await sleep(1000).then(next);
next()
console.log('middleware1 end', new Date().getTime());
})
router.use(async function (ctx, next) {
console.log('middleware2 start', new Date().getTime());
// await sleep(2000).then(next);
next()
console.log('middleware2 end', new Date().getTime());
})
router.get('/', (ctx, next) => {
ctx.body = 'hello koa';
});
app.listen(8888, function () {
console.log('server start');
})
/**
* 启动服务时会调用callback
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
callback中调用compose方法获取promise处理后的中间件
callback() {
const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error', this.onerror);
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
compose中dispatch递归调用
function compose (middleware) {
if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
for (const fn of middleware) {
if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
}
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
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 {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
use 的时候会把中间件放到数组中this.middleware.push(fn)
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-');
this.middleware.push(fn);
return this;
}
*/
/** async await
middleware1 start 1639029988309
middleware2 start 1639029989311
middleware2 end 1639029991316
middleware1 end 1639029991316
*/
/**
middleware1 start 1639030056718
middleware2 start 1639030056718
middleware2 end 1639030056719
middleware1 end 1639030056719
*/