LazyMan("Hank")
// Hi! This is Hank!
LazyMan("Hank").sleep(10).eat("dinner")
// Hi! This is Hank!
// 等待10 秒..
// Wake up after 10
// Eat dinner~
LazyMan("Hank").eat("dinner").eat("supper")
// Hi This is Hank!
// Eat dinner~
// Eat supper~
LazyMan("Hank").sleepFirst(5).eat("supper")
// 等待 5 秒
// Wake up after 5
// Hi This is Hank!
// Eat supper
sleepFirst 最为特殊,这个任务或者这个方法的 优先级最高 ;调用 sleepFirst 之后,链式调用将暂停一定时间后继续执行。请再次观察题干,尤其是最后一个 demo,sleepFirst 的输出优先级最高,调用后先等待 5 秒输出 Wake up after 5,再输出 Hi This is Hank!
proto.handle = function handle(req, res, out) {
var index = 0;
var protohost = getProtohost(req.url) || '';
var removed = '';
var slashAdded = false;
var stack = this.stack;
// final function handler
var done = out || finalhandler(req, res, {
env: env,
onerror: logerror
});
// store the original URL
req.originalUrl = req.originalUrl || req.url;
function next(err) {
// ...
}
next();
};
源码导读:out 参数是关于 sub app 的特性,这个特性可以暂时忽略,我们暂时不关心。handle 实现我们并不陌生,它构建 next 函数,并触发第一个 next 执行。
next 实现:
function next(err) {
if (slashAdded) {
req.url = req.url.substr(1);
slashAdded = false;
}
if (removed.length !== 0) {
req.url = protohost + removed + req.url.substr(protohost.length);
removed = '';
}
// next callback
var layer = stack[index++];
// all done
if (!layer) {
defer(done, err);
return;
}
// route data
var path = parseUrl(req).pathname || '/';
var route = layer.route;
// skip this layer if the route doesn't match
if (path.toLowerCase().substr(0, route.length) !== route.toLowerCase()) {
return next(err);
}
// skip if route match does not border "/", ".", or end
var c = path.length > route.length && path[route.length];
if (c && c !== '/' && c !== '.') {
return next(err);
}
// trim off the part of the url that matches the route
if (route.length !== 0 && route !== '/') {
removed = route;
req.url = protohost + req.url.substr(protohost.length + removed.length);
// ensure leading slash
if (!protohost && req.url[0] !== '/') {
req.url = '/' + req.url;
slashAdded = true;
}
}
// call the layer handle
call(layer.handle, route, err, req, res, next);
}
源码导读:
取出下一个中间件
var layer = stack[index++]
如果当前请求路由和 handler 不匹配,则跳过:
if (path.toLowerCase().substr(0, route.length) !== route.toLowerCase()) {
return next(err);
}
Function.prorotype.before = function (fn) {
const self = this
return function (...args) {
console.log('')
let res = fn.call(this)
if (res) {
self.apply(this, args)
}
}
}
Function.prototype.after = function (fn) {
const self = this
return function (...args) {
let res = self.apply(this, args)
if (res) {
fn.call(this)
}
}
}
function compose(middleware) {
return function *(next) {(
if (!next) next = noop();
var i = middleware.length;
while (i--) {
next = middleware[i].call(this, next);
console.log('isGenerator:', (typeof next.next === 'function' && typeof next.throw === 'function')); // true
}
return yield *next;
}
}
function *noop(){}
其中,一个中间件的写法类似:
app.use(function *(next){
var start = new Date;
yield next;
var ms = new Date - start;
this.set('X-Response-Time', ms + 'ms');
});
这是一个很简单的记录 response time 的中间件,中间件跳转的信号是 yield next。
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) {
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, function next () {
return dispatch(i + 1)
}))
} catch (err) {
return Promise.reject(err)
}
}
}
}