美文网首页Koa
Koa(五、源码浅析)

Koa(五、源码浅析)

作者: 强某某 | 来源:发表于2018-06-22 09:05 被阅读3次

    基础http请求

    const http=require("http");
    const app=http.createServer((req,res)=>{
        res.writeHead(200);
        res.end("Hello");
    })
    app.listen(3000,()=>{
        console.log("listen to 3001")
    })
    

    针对http基础请求简单封装

    Application.js:
    
    const http=require('http');
    class Application{
        constructor(){
            this.callback=''
        }
        use(callback){
            this.callback=callback
        }
        listen(...args){
            let app=http.createServer((req,res)=>{
                this.callback(req,res);
            })
            app.listen(...args)
        }
    }
    module.exports=Application
    
    
    test.js:
    
    const Qow=require('./Application');
    const app=new Qow();
    app.use((req,res)=>{
        res.writeHead(200);
        res.end("Hello");
    }) 
    app.listen(3001,()=>{
        console.log("listen to 3001")
    })
    

    效果上面两者相同,下面继续封装

    js的getter和setter

    const zq={
        _name:"zq",
        get name(){
            return this._name
        },
        set name(name){
            this._name=name
        }
    }
    console.log(zq.name)
    zq.name="zq1"
    console.log(zq.name)
    说明:类似于java的get,set,其实_name还可以使用,
    只不过规范定义为下划线标志为私有,最好别使用。
    

    封装ctx的阶段性代码

    Application.js:
    const http = require("http")
    let response = {
        get body() {
            return this._body;
        },
        set body(val) {
            this._body = val
        }
    }
    let request = {
        get url() {
            return this.req.url;
        }
    }
    let context = {
        get body() {
            return this.response.body;
        },
        set body(val) {
            this.response.body = val;
        },
        get url() {
            return this.request.url;
        }
    }
    class Application {
        constructor() {
            this.context = context
            this.request = request
            this.response = response
        }
        use(callback) {
            this.callback = callback;
        }
        listen(...args) {
    //作用:async作用是因为内部的callback可能是异步操作
            let app = http.createServer(async (req, res) => {
                let ctx = this.createCtx(req, res)
                await this.callback(ctx);
    //在callbak内部,也就是test.js中ctx.body已经被重新赋值,所以下面可以直接使用
                ctx.res.end(ctx.body)
            })
            app.listen(...args)
        }
        createCtx(req, res) {
            let ctx = Object.create(this.context);
            ctx.request = Object.create(this.request);
            ctx.response = Object.create(this.response)
     //作用:就是类似koa的ctx直接可使用一些属性,同时request/response也都可以使用
            ctx.req = ctx.request.req = req;
            ctx.res = ctx.response.res = res;
            return ctx;
        }
    }
    module.exports = Application
    
    test.js:
    const Qow=require('./demo/a');
    const app=new Qow();
    app.use(async ctx=>{
        ctx.body='Hello'
    }) 
    app.listen(3001,()=>{
        console.log("listen to 3001")
    })
    

    说明:Obejct.create作用就是复制一份一样的,因为上面的context等都是在类外面,使用时候导入在内存当中只有一份,每次new对象的时候,都是独立的,所以需要复制一份在当前的对象中

    嵌套调用

    function add(x,y) {
        return x+y
    }
    function double(z){
        return z*2
    }
    const result=double(add(1,2))
    console.log(result)//6
    

    同步中间件模拟

    function add(x,y) {
        return x+y
    }
    function double(z){
        return z*2
    }
    const middlewares=[add,double];
    let len=middlewares.length;
    function compose(midds){
        return (...args)=>{
            //初始化默认执行第一个函数(后期就是中间件)
            let res=midds[0](...args)
            for (let i = 1; i < len; i++) {
                res=midds[i](res) 
            }
            return res;
        }
    }
    const fn=compose(middlewares)
    const result=fn(1,2)
    console.log(result)//6
    说明:该逻辑和上面的js模块效果相同都是串联执行,把上一次结果当成下一次的参数
    

    中间件模拟

    //异步的测试
    async function fn1(next){
        console.log('fn1');
        await next()
        
        console.log('end fn1');
    }
    async function fn2(next){
        console.log('fn2');
        await delay()
        await next()
        console.log('end fn2');
    }
    function fn3(next){
        console.log('fn3');
    }
    function delay(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve()
            },2000)
        })
    }
    function compose(middlewares){
        return function () {
            return dispatch(0)
            function dispatch(i){
                let fn=middlewares[i];
                if(!fn){
                    return Promise.resolve()
                }
                return Promise.resolve(fn(function next() {
                    return dispatch(i+1)
                }))
            } 
        }
    }
    const middlewares=[fn1,fn2,fn3];
    const final=compose(middlewares);
    final()
    输出:输出 fn1 fn2 延时两秒 fn3 end fn2 end fn1
    

    说明:核心就是compose方法,Promise.resolve作用就是立即执行该方法,然后返回一个Promise
    if里面是判断条件,如果中间件已经递归完毕则退出并返回一个promise,fn就是中间件,fn内部回调
    是next函数,所以如果在调用时候,如果next不手动调用,则不进入下一个中间件,因为dispatch不会
    被继续递归调用。
    如果把fn1中next的await去掉,输出就是fn1 fn2 延时两秒 fn3 end fn1 end fn2
    async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到
    await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
    next其实就是返回的dispatch就是一个promise,如果不加await就会立即执行完毕然后返回promise,
    根据下图可知,fn0输出之后next相当于内部调用dispatch(1),然后其内部next相当于调用dispatch(2).
    没有加await在js的事件轮询里面,就不会等待回调执行完毕才开启执行,执行所有next之前的顺序执行完毕之后,因为调用栈的原因,回调肯定靠后执行,此时没加await的next后面的语句会先执行完,然后下次轮询才会从回调队列找出回调的依次执行。

    中间件洋葱图模拟.png

    精简版本Koa实现和测试

    Application.js:
    
    const http = require("http")
    let response = {
        get body() {
            return this._body;
        },
        set body(val) {
            this._body = val
        }
    }
    let request = {
        get url() {
            return this.req.url;
        }
    }
    let context = {
        get body() {
            return this.response.body;
        },
        set body(val) {
            this.response.body = val;
        },
        get url() {
            return this.request.url;
        }
    }
    class Application {
        constructor() {
            this.context = context
            this.request = request
            this.response = response
            this.middlewares=[]
        }
        use(callback) {
            this.middlewares.push(callback)
            // this.callback = callback;
        }
        listen(...args) {
            let app = http.createServer(async (req, res) => {
                let ctx = this.createCtx(req, res)
                const fn=this.compose(this.middlewares)
                await fn(ctx);
                ctx.res.end(ctx.body)
            })
            app.listen(...args)
        }
        compose(middlewares){
            return function (context) {
                return dispatch(0)
                function dispatch(i){
                    let fn=middlewares[i];
                    if(!fn){
                        return Promise.resolve()
                    }
                    return Promise.resolve(fn(context,function next() {
                        return dispatch(i+1)
                    }))
                } 
            }
        }
        createCtx(req, res) {
            let ctx = Object.create(this.context);
            ctx.request = Object.create(this.request);
            ctx.response = Object.create(this.response);
            ctx.req = ctx.request.req = req;
            ctx.res = ctx.response.res = res;
            return ctx;
        }
    }
    module.exports = Application
    
    test.js:
    const Qow=require('./demo/a');
    const app=new Qow();
    app.use(async (ctx,next)=>{
        ctx.body='1'
        await next()
        ctx.body+='5'
    })
    app.use(async (ctx,next)=>{
        ctx.body+='2'
        await next()
        ctx.body+='4'
    }) 
    app.use(async ctx=>{
        ctx.body+='3'
    }) 
    app.listen(3001,()=>{
        console.log("listen to 3001")
    })
    网页输出:12345
    

    相关文章

      网友评论

        本文标题:Koa(五、源码浅析)

        本文链接:https://www.haomeiwen.com/subject/vdwhyftx.html