1. 入口文件
- 在package.json中通过 `main` 属性得知入口文件是 `lib/application.js`
2. 核心文件
- 核心文件都在 `lib` 文件夹中
- lib/application.js --> 主要就是Koa类相关代码,即 new Koa()
- lib/context.js ------> 是context对象相关
- lib/request.js ------> request相关
- bli/response.js -----> response相关
3. 依赖
- koa-compose ---------> 处理中间件
- on-finished ---------> 主要作用:当 HTTP 请求关闭、完成或出错时执行回调
- 本项目已经做好了调试配置,只需要执行
cnpm run dev
断点调试 index.js
文件即可
- 其他测试在 test-koa 中
- 比如 http.createServer
- 比如 中间件的执行属性问题(洋葱模型)
1. 克隆koa源码:git clone git@github.com:koajs/koa.git
2. 安装依赖:cnpm install
3. 新建 index.js,并写入示例代码
4. 在 `vscode` 中选择 `运行和调试`, 新建 `launch.json` ,选择 `node`,并做如下配置
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动程序",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/index.js"
}
]
}
5. 启动项目:node index.js
6. 在index.js中打断点,通过 `运行和调试` 菜单中的 `开始调试` 按钮进行断点吊饰即可
- 中间件
- 中间件执行顺序测试
cnpm run order
- 中间键执行顺序测试,对应文件
test/index-order-test.js
- node.js
- http.createServer
cnpm run createServer
- http.createServe,对用文件
test/createServer
- node.js事件循环顺序
- process.nextTick()
cnpm run eventLoop
- 调用顺序:app.listen() ---> callback() ---> handleRequest() ---> 中间件fn(ctx).then(handleResponse).catch(onerror)
- fnMiddleware = compose(this.middleware) = function (context, next) => dispatch(0)
中间件 - 具体的执行过程
1. fnMiddleware(ctx).then(handleResponse).catch(onerror)
2. (function (context, next) => dispatch(0)).then(handleResponse).catch(onerror)
3. dispatch(0).then(handleResponse).catch(onerror)
4. Promise.resolve(middlewareFn0(context, dispatch.bind(null,1)))
5. Promise.resolve(async(ctx, dispatch) => {... dispatch(1) ...})
6. 最终形态如下
const [fn1, fn2, fn3] = this.middleware()
const fnMiddleware = function(context, next) {
return Promise.resolve(fn1(context, function next1() {
return Promise.resolve(fn2(context, function next2() { // 每个中间件,如果存在next就return Promise.resolve(fn(context, dispatch.bind(null, i + 1))),不存在就 return Promise.resolve()
return Promise.resolve(fn3(context, function next3() { // 最后一个中间件没有next函数了,因为已经是最后一个
return Promise.resolve()
}))
}))
}))
}
7. fnMiddleware() 执行的最终状态如下
- 7.1
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(2);
});
app.use(async (ctx, next) => {
console.log(3);
await next();
console.log(4);
});
app.use(async (ctx, next) => {
console.log(5);
ctx.body = "测试中间执行顺序";
});
- 7.2
fnMiddleware() => Promise.resolve(
// fn1()
console.log(1)
await Promise.resolve( // next()
// fn2()
console.log(3)
await Promise.resolve( // next()
// fn3()
console.log(5)
return Promise.resolve()
)
console.log(4)
)
console.log(2)
)
.then(handleResponse)
.catch(onerror)
// 13542
2
process.nextTick
函数签名:process.nextTick(callback[, ...args])
作用:微任务 - 在同步方法执行完毕后,下一轮事件循环中的开始执行
特点:
- 执行时机在同步任务之后,在异步任务宏任务setTimeout之前
- 其实process.nextTick()会在node事件循环的各个周期优先执行
参数:
- 1. callback回调函数
- 2. args当调用callback时要传入的额外参数
3
node.js事件轮训机制 - 一共分为6个阶段
(1) timers 定时器阶段
- ( 计时 ) 和 ( 执行到点的定时器 )
(2) pending callbacks 阶段
- 执行某些系统操作的回调函数,比如 ( tcp错误类型 )
(3) idle, prepare 阶段
- 一些准备工作
(4) poll轮询阶段,是一个轮询队列
- 1. 如果 ( 轮询队列不为空 ),依次取出执行,直到 ( 轮询队列为空 ) 或者 ( 达到系统最大限制 )
- 2. 如果 ( 轮询队列为空 )
- 1. 如果之前设置过 ( setImmediate ) 函数,则直接进入下一个阶段 ( check阶段 )
- 2. 如果之前没有设置过setImmediate函数,则会在当前poll阶段 ( 等待 )
- 直到 ( 轮询队列 ) 添加进了新的回调函数,那么就会进入(4)阶段1的判断,继续执行
- 或者 ( 定时器 ) 到点了,也会进入下一个阶段 ( check阶段 )
(5) check 阶段
- 执行 ( setImmediate ) 回调函数
(6) close callbacks 阶段
- 执行 ( close ) 事件回调函数
-------> 注意点:process.nextTick() 会在nodejs事件轮询的 ( 任意阶段,优先执行 )
---
案例
console.log(1); // 同步任务
setTimeout(() => console.log(2)); // timer阶段执行 - nodejs事件轮询的第 1 个阶段
setTimeout(() => console.log(8), 0); // timer阶段执行 - nodejs事件轮询的第 1 个阶段
process.nextTick((n) => console.log(n), 3); // --- 在 node.js 事件轮询的 ( 任意阶段,优先执行 ),即在同步任务执行完毕后,优先执行
setImmediate(() => console.log(4)); // check阶段执行 - nodejs事件轮询的第 5 个阶段
new Promise((resolve) => {
console.log(5); // 同步任务
resolve();
console.log(7); // 同步任务
}).then((res) => console.log(6)); // --- 微任务
// 执行顺序 1 5 7 3 6 2 8 4
// 同步任务 1 5 7
// 异步任务(微任务) 3 6
// 异步任务(宏任务) 2 8 4
1
测试:本项目/index.js
官网:https://koa.bootcss.com/#application
2
分析官网案例
---
const app = new Koa()
- 执行构造函数
app.use(async ctx => { ctx.body = 'Hello World'; });
- 源码中use方法如下
use(fn) {
this.middleware.push(fn); // 向 middleware 中添加中间件
return this; // 返回 Koa 实例,则可以链式调用 app.use()
}
- app.use
- app.use(async (ctx, next) => { console.log(1); await next(); console.log(2) })
- 扩展
- koa中间件的执行顺序
- https://juejin.cn/post/7008056344540348453
app.listen(3000);
- 源码中listen方法如下
listen(...args) {
const server = http.createServer(this.callback());
return server.listen(...args);
}
- 解析
- http.createServer
- 是node.js提供的原生api
- 使用:http.createServer((req, res) => { ...... })
- 本项目中使用案例:/test-http.createServer.js
- 官网说明:http://nodejs.cn/api/http.html#http_http_createserver_options_requestlistener
- **总流程**
- app.listen() -> http.createServer(this.callback()) -> fnMiddleware(ctx).then(handleResponse).catch(onerror) -> compose(this.middleware) -> dispatch() -> middleware(i) -> dispatch(i+1)