zoukankan      html  css  js  c++  java
  • Node 浅析koa2中间件

    koa2采用asyncawait来处理异步,koa2实例的use函数的参数都是中间件。

    先来看一个koa2的核心小demo

    // 中间件的仓库
    const arr = [
        async (next) => {
            console.log(1)
            await next()
            console.log(2)
        },
        async (next) => {
            console.log(3)
            await new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(
                        console.log(4)
                    )
                }, 1000)
            }) // 异步操作 await 会等待后面的promise resolve 后再向下执行
            await next()
            console.log(5)
        },
        async (next) => {
            console.log(6)
        },
        async (next) => {
            // 不会执行 因为上一个函数中没有执行next
            console.log(7)
            await next()
            console.log(8)
        },
        async (next) => {
            // 不会执行 因为前面的函数中没有执行next
            console.log(9)
        }
    ]
    
    function fun(arr) {
        function dispose(index) {
            const currentFun = arr[index]
            const next = dispose.bind(null, index + 1)
            return currentFun(next) // 尾递归
        }
    
        dispose(0)
    }
    
    fun(arr) // 先打印 1 3 一秒后打印4 6 5 2
    

    code开始 (新建一个ware.js文件)

    const http = require('http')
    const urlParser = require("url") // 解析url字符串和url对象
    
    class Middleware {
        constructor() {
            this.wares = []  // 存储中间件
        }
    
        use(fun) {
            this.wares.push(fun) // 收集中间件
            return this
        }
    
        /* 中间件处理的核心 */
        handleMiddleware(wareList) {
            return ctx => {
                // 中间件调用
                const dispose = index => {
                    const currentFun = wareList[index]
                    return new Promise((resolve, reject) => {
                        try {
                            // 使用Promise.resolve 包装 currentFun 防止外部传入的currentFun为一个普通函数
                            /* dispose.bind(null, index + 1)就是next 让dispose继续执行下一个中间件
                                如果没有在中间件中调用dispose.bind(null, index + 1) 则不会再去获取下一个中间件
                            */
                            return resolve(currentFun(ctx, dispose.bind(null, index + 1)))
                        } catch (e) {
                            return Promise.reject(e)
                        }
                    })
                }
    
                // 立即执行一下仓库的第一个中间件
                dispose(0)
            }
        }
    
        createContext(req, res) {
            const {method, url} = req
            const {query} = urlParser.parse(url, true)
    
            // ... 这里远比这个复杂, 我们只做一个简单的包装
            return {
                method, url, query,
                res
            }
        }
    
        serverHandle() {
            return (req, res) => {
                // 当请求来的时候我们去触发中间件
                const fn = this.handleMiddleware(this.wares)
                // 得到当前请求的上下文对象
                const ctx = this.createContext(req, res)
                fn(ctx)
            }
        }
    
        listen(...args) {
            const app = http.createServer(this.serverHandle()) // 这里只是为了模拟得到一个http服务
            app.listen(...args) // 直接交给node原生的http模块处理
        }
    }
    
    module.exports = Middleware
    

    测试 (同一目录下新建一个demo.js文件)

    const Demo = require("./ware")
    
    const app = new Demo()
    
    app.use(async (ctx, next) => {
        await next()
        console.log(`${ctx.method} ${ctx.url}`)
    })
    
    app.use(async ctx => {
        ctx.res.end("hello world")
    })
    
    app.listen(5000, () => {
        console.log("http://localhost:5000")
    })
    

    > cmd运行 node demo.js 浏览器访问 http://localhost:5000 => hello world

    为之则易,不为则难。
  • 相关阅读:
    Grove.net实践ORM学习笔记
    COM+的事务
    Delphi中MIDAS线程模型
    Delphi中封装ADO之我重学习记录。。。
    100 多个JaveScript 常用函数
    javascript 事件
    js 收藏
    js 常用函数
    表单11种Input的高级用法
    UltraEdit 使用技巧
  • 原文地址:https://www.cnblogs.com/coderDemo/p/13471393.html
Copyright © 2011-2022 走看看