const http = require('http'); const server = http.createServer((req, res) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end('Hello World\n'); }); server.listen(3000); 复制代码
nodejs主要的原生模块http,fs,path;基于http模块http模块的createServer方法建立一个http.server实例,函数会自动绑定到request事件,最后server监听3000端口,处理请求。request事件包括两个参数req,res;req可写流,res可读流,经过req请求,res回应。node
事件驱动四类解决方式git
var i = 0;//记录sleep()函数调用的次数 function sleep(ms, callback){ setTimeout(function(){ if(i < 2){ i++; callback("finish", null); }else{ callback(null, new Error('i>2')); } }, ms); } //第一次调用 sleep(1000, function (val, err) { if (err) console.log(err.message); else { console.log(val); //第二次调用 sleep(1000, function (val, err) { if (err) console.log(err.message); else { console.log(val); //第三次调用 sleep(1000, function (val, err) { if (err) console.log(err.message); else { console.log(val); } }); } }); } });//输出结果分别为:finish,finish,i>2。 复制代码
var i = 0; var events = require('events'); var emitter = new events.EventEmitter();//建立事件监听器的一个对象 function sleep(ms) { var emitter = new require('events')(); setTimeout(function () { console.log('finish!'); i++; if (i > 2) emitter.emit('error', new Error('i>2')); else emitter.emit('done', i); }, ms); } var emit = sleep(1000); emit.on('done',function (val) { console.log('成功:' + val); }) emit.on('error',function(err){ console.log('出错了:' + err.message); }) 复制代码
对于callback的改进,使用事件监听的形式进行操做。每次调用异步函数都会返回一个EvetEmitter对象。在函数内部,能够根据需求来触发不一样的事件。在函数外部对不一样的事件进行监听,而后作出相应的处理。github
var i = 0; function sleep(ms) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log('finished'); i++; if (i > 2) reject(new Error('i>2')); else resolve(i); }, ms); }) } sleep(1000).then(function (val) { console.log(val); return sleep(1000) }).then(function (val) { console.log(val); return sleep(1000) }).then(function (val) { console.log(val); return sleep(1000) }).catch(function (err) { console.log(err.message); }) 复制代码
promise相似只触发两个事件resolve和reject的event对象,可是不一样的是事件具备即时性,触发以后这个状态后事件就消失了。将本来层层嵌套的回调函数展开,视觉效果更好。web
var i = 0; function sleep(ms) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log('finished'); i++; if (i >= 2) reject(new Error('i>2')); else resolve(i); }, ms); }) } (async function () { try { var val; val = await sleep(1000); console.log(val); val = await sleep(1000); console.log(val); val = await sleep(1000); console.log(val); } catch (err) { console.log(err.message); } } ()) 复制代码
await关键字只能在async函数中才能使用,也就是说你不能在任意地方使用await。await关键字后跟一个promise对象,函数执行到await后会退出该函数,直到事件轮询检查到Promise有了状态resolve或reject 才从新执行这个函数后面的内容。数据库
Koa is a new Web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation for Web applications and APIs.express
Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 经过利用 async 函数,Koa 帮你丢弃回调函数,并有力地加强错误处理。 Koa 并无捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。npm
const Koa = require('koa'); const app = new Koa(); // logger app.use(async (ctx, next) => { await next(); const rt = ctx.response.get('X-Response-Time'); console.log(`${ctx.method} ${ctx.url} - ${rt}`); }); // x-response-time app.use(async (ctx, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; ctx.set('X-Response-Time', `${ms}ms`); }); // response app.use(async ctx => { ctx.body = 'Hello World'; }); app.listen(3000); 复制代码
koa只封装上下文,请求,响应的中间件容器,koa1基于generator,koa2基于async/await实现瀑布流式开发方式。编程
面向切面编程(AOP)是一种非侵入式扩充对象、方法和函数行为的技术。经过 AOP 能够从“外部”去增长一些行为,进而合并既有行为或修改既有行为。api
原始流程 数组
var koa = require('koa'); var app = new koa(); app.use(async (ctx, next) => { console.log(1) await next(); console.log(5) }); app.use(async (ctx, next) => { console.log(2) await next(); console.log(4) }); app.use(async (ctx, next) => { console.log(3) ctx.body = 'Hello World'; }); // 访问http://localhost:3000 // 打印出一、二、三、四、5 复制代码
async/await相对于express的主流promise写法是更好的callback hell解决方式,同时,结合使用trycatch更方便定位错误。
koa2的洋葱圈设计模型相对express来讲方便处理后置逻辑,express是线性的处理流程,在中间件的编写上,koa更简洁,异步越多,优点越明显。
.
├── application.js
├── context.js
├── request.js
└── response.js
复制代码
入口
... const response = require('./response'); const compose = require('koa-compose'); const context = require('./context'); const request = require('./request'); const Emitter = require('events'); ... // 暴露出来继承自event.Emitter的方法,提供给用户新建实例 module.exports = class Application extends Emitter { constructor() { super(); this.proxy = false; // 是否信任proxy header,默认false this.middleware = []; // 保存经过app.use(middleware)注册的中间件 this.subdomainOffset = 2; //配置忽略的.subdomains this.env = process.env.NODE_ENV || 'development'; // 环境变量,默认为 NODE_ENV 或 ‘development’ this.context = Object.create(context); // context模块,经过context.js建立 this.request = Object.create(request); // request模块,经过request.js建立 this.response = Object.create(response); // response模块,经过response.js建立 } ... 复制代码
1.原生nodejs中90%以上的方法继承自event.Emitter。由于是事件驱动,event.Emitter中主要有on/addListener,emit,,removeListeners,removeAllListeners等事件钩子;由于事件驱动,大部分状况下是黑盒,因此提供钩子,不用关注具体状态,只关注具体事件点便可。
2.使用object.create不会保留原构造函数的属性,不执行原构造函数。object.create的好处同时也减小内存消耗。
use
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 || '-'); //debug node包,至关于console.log(),可配置开发开启,上线自动过滤 this.middleware.push(fn); return this; } 复制代码
koa2提供了对koa1generator类型中间件的适配,use主要是用于收集中间件到middleware中。
listen(...args) { debug('listen'); const server = http.createServer(this.callback()); return server.listen(...args); } 复制代码
依照原生模块封装了listen方法,链式调用(扩展运算符apply),在使用以前就调用this.callback()方法——初始化中间件,造成上下文对象。
callback() { //经过compose()来处理middleware返回的是一个函数 const fn = compose(this.middleware); console.log(this.listenerCount) if (!this.listenerCount('error')) this.on('error', this.onerror); //listenerCount从Emitter里继承来,作错误处理 const handleRequest = (req, res) => { const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return handleRequest; } 复制代码
使用koa-compose模块来处理use接收来的middleware,compose方法接收参数是函数的数组,返回一个执行后返回Promise.resolve...
经过createContext函数以及context等模块将req和res和并出一个context(ctx)方便开发者处理请求响应。
this.handleRequest处理接收request请求时的一些方法。
callcack()先执行,返回的handleRequest函数在当有request请求时进行响应,同时处理response请求。(主要就是提早给各事件提早增长监听器关系)
function compose(middleware) { //middleware必须是一个数组 if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!') for (const fn of middleware) { // middleware的每个元素都必须是函数 if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') } return function (context, next) { let index = -1//index记录已处理元素 return dispatch(0)// 从数组的第一个元素开始dispatch 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()//最后处理的中间件还有next的状况处理 try { return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err) } } } } 复制代码
Promise.resolve(function(context, next){ console.log(1) await next(); console.log(5) }()); 复制代码
Promise.resolve(function(context, 中间件2){ console.log(1) await Promise.resolve(function(context, next){ console.log(2) await next(); console.log(4) }()) console.log(5) }()); 复制代码
Promise.resolve(function(context, 中间件2){ console.log(1) await Promise.resolve(function(context, next){ console.log(2) await Promise.resolve(function(context){ console.log(5) }()) console.log(4) }()) console.log(5) }()); 复制代码
createContext(req, res) { const context = Object.create(this.context); const request = context.request = Object.create(this.request); const response = context.response = Object.create(this.response); context.app = request.app = response.app = this; context.req = request.req = response.req = req; context.res = request.res = response.res = res; request.ctx = response.ctx = context; request.response = response; response.request = request; context.originalUrl = request.originalUrl = req.url; context.state = {}; return context; } 复制代码
createContext主要是挂载request和response以及自建处理方法到context上下文对象中,集成请求响应减小开发成本;须要注意的是state对象提供给了中间件记录状态,提升中间件效率。
handleRequest(ctx, fnMiddleware) { const res = ctx.res; res.statusCode = 404;//默认处理状态,未处理则404 const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx); //主要处理http请求的一些收尾工做 onFinished(res, onerror); return fnMiddleware(ctx).then(handleResponse).catch(onerror); } 复制代码
delegate(proto, 'response') .method('attachment') .method('redirect') .method('remove') .method('vary') .method('set') .method('append') .method('flushHeaders') .access('status') .access('message') 复制代码
koa2的属性代理,主要方便开发者更容易获取到一些属性。由delegate模块帮助实现。
koa-generator相似express风格的脚手架,可帮助快速配置建成nodejs应用
npm install -g koa-generator koa2 /tmp/foo && cd /tmp/foo npm install ? npm start 复制代码
app.use(async (context, next) => { if (context.request.method === 'OPTIONS') { context.response.status = 200 context.response.set('Access-Control-Allow-Origin', context.request.headers.origin) context.response.set('Access-Control-Allow-Headers', 'content-type') } else { await next()//别忘记next() } }) //异步操做要用promise封装 const findAllUsers = () => { return new Promise((resolve, reject) => { User.find({}, (err, doc) => { if (err) { reject(err); } resolve(doc); }); }); }; app.use(async (context, next) => { // 异步操做数据库 let result = await findAllUsers() next() //next操做不是和await链接使用的。 context.response.status = 200 context.response.set('Access-Control-Allow-Origin', context.request.headers.origin) context.response.set('Access-Control-Allow-Headers', 'content-type') context.response.message = '读取成功' ctx.body = { succsess: '成功', result }; }) app.use(async (context, next) => { // 异步操做数据库 console.log('test') }) 复制代码