node 淺析koa2中介軟體
阿新 • • 發佈:2020-08-10
koa2採用裡async
和await
來處理非同步,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 function (ctx) { // 中介軟體呼叫 function dispose(index) { const currentFun = wareList[index]; try { // 使用Promise.resolve 包裝 currentFun 防止外部傳入的currentFun為一個普通函式 return Promise.resolve( /* dispose.bind(null, index + 1)就是next 讓dispose繼續執行下一個中介軟體 如果沒有在中介軟體中呼叫dispose.bind(null, index + 1) 則不會再去獲取下一個中介軟體 */ 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"); });