koa中間件機制詳解

轉自:https://cnodejs.org/topic/58fd8ec7523b9d0956dad945

koa是由express原班人馬打造的一個更小、更富有表現力、更健壯的web框架。

在我眼中,koa的確是比express輕量的多,koa給我的感覺更像是一個中間件框架,koa只是一個基礎的架子,需要用到的相應的功能時,用相應的中間件來實現就好,諸如路由系統等。一個更好的點在于,express是基于回調來處理,至于回調到底有多么的不好,大家可以自行搜索來看。koa1基于的co庫,所以koa1利用Generator來代替回調,而koa2由于nodeasync/await的支持,所以koa2利用的是async/await。關于async以及co庫等,大家可以參考我之前寫過的一篇文章(理解async)。koa可以說是一個各種中間件的架子,下面就來看一下koa對于中間件部分的實現:?

koa1的中間件

koa1主要利用的是Generator來實現,一般來說,koa1的一個中間件大概是長這個樣子的:

app.use(function *(next){console.log(1);yield next;console.log(5);
});
app.use(function *(next){console.log(2);yield next;console.log(4);
});
app.use(function *(){console.log(3);
});

這樣的輸出會是1, 2, 3, 4, 5koa的中間件的實現主要依靠的是koa-compose

function compose(middleware){return function *(next){if (!next) next = noop();var i = middleware.length;// 組合中間件while (i--) {next = middleware[i].call(this, next);}return yield *next;}
}
function *noop(){}

源碼非常的簡單,實現的功能就是將所有的中間件串聯起來,首先給倒數第一個中間件傳入一個noop作為其next,再將這個整理后的倒數第一個中間作為next傳入倒數第二個中間件,最終得到的next就是整理后的第一個中間件。說起來比較復雜,畫圖來看:

?

實現的效果如同上圖,與redux需要實現的目標類似,只要遇到了yield next就去執行下一個中間件,利用co庫很容易將這個流程串聯起來,下面來簡單模擬下,中間件完整的實現:

?

const middlewares = [];const getTestMiddWare = (loggerA, loggerB) => {return function *(next) {console.log(loggerA);yield next;console.log(loggerB);}
};
const mid1 = getTestMiddWare(1, 4),mid2 = getTestMiddWare(2, 3);const getData = new Promise((resolve, reject) => {setTimeout(() => resolve('數據已經取出'), 1000);
});function *response(next) {// 模擬異步讀取數據庫數據const data = yield getData;console.log(data);
}middlewares.push(mid1, mid2, response);
// 簡單模擬co庫
function co(gen) {const ctx = this,args = Array.prototype.slice.call(arguments, 1);return new Promise((reslove, reject) => {if (typeof gen === 'function') gen = gen.apply(ctx, args);if (!gen || typeof gen.next !== 'function') return resolve(gen);const baseHandle = handle => res => {let ret;try {ret = gen[handle](res);} catch(e) {reject(e);}next(ret);};const onFulfilled = baseHandle('next'),onRejected = baseHandle('throw');onFulfilled();function next(ret) {if (ret.done) return reslove(ret.value);// 將yield的返回值轉換為Proimselet value = null;if (typeof ret.value.then !== 'function') {value = co(ret.value);} else {value = ret.value;}if (value) return value.then(onFulfilled, onRejected);return onRejected(new TypeError('yield type error'));}});
}
// 調用方式
const gen = compose(middlewares);
co(gen);

koa2的中間件

隨著node對于async/await的支持,貌似不需要再借助于co這種工具庫了,直接利用原生的就好,于是koa也做出了改變,來看目前的koa-compose

function compose (middleware) {// 參數檢驗return function (context, next) {// last called middleware #let index = -1return dispatch(0)function dispatch (i) {if (i <= index) return Promise.reject(new Error('next() called multiple times'))index = ilet fn = middleware[i]// 最后一個中間件的調用if (i === middleware.length) fn = nextif (!fn) return Promise.resolve()// 用Promise包裹中間件,方便await調用try {return Promise.resolve(fn(context, function next () {return dispatch(i + 1)}))} catch (err) {return Promise.reject(err)}}}
}

koa-compose利用了Promisekoa2的中間件的參數也有一個變為了兩個,而且執行下一個的中間件利用的是await next(),要達到與上面的示例代碼的相同效果,需要更改中間件的寫法:

?

const middlewares = [];
const getTestMiddWare = (loggerA, loggerB) => async (ctx, next) => {console.log(loggerA);await next();console.log(loggerB);
};const mid1 = getTestMiddWare(1, 4),mid2 = getTestMiddWare(2, 3);
const response = async () => {// 模擬異步讀取數據庫數據const data = await getData();console.log(data);
};
const getData = () => new Promise((resolve, reject) => {setTimeout(() => resolve('數據已經取出'), 1000);
});
middlewares.push(mid1, mid2);// 調用方式
compose(middlewares)(null, response);

如何做到兼容

可以看到的是,koa1koa2對于中間件的實現還是有著很多的不同的,將koa1的中間件直接拿到koa2下面來使用肯定是會出現錯誤的,如何兼容這兩個版本也成了一個問題,koa團隊寫了一個包來是koa1的中間件可以用于koa2中,叫做koa-convert,先來看看這個包怎么使用:

function *mid3(next) {console.log(2, 'koa1的中間件');yield next;console.log(3, 'koa1的中間件');
}
convert.compose(mid3)

來看下這個包實現的思路:

// 將參數轉為數組,對每一個koa1的中間件執行convert操作
convert.compose = function (arr) {if (!Array.isArray(arr)) {arr = Array.from(arguments)}return compose(arr.map(convert))
}
// 關鍵在于convert的實現
const convert = mw => (ctx, next) => {// 借助co庫,返回一個Promise,同時執行yieldreturn co.call(ctx, mw.call(ctx, createGenerator(next)));
};function * createGenerator (next) {/*next為koa-compomse中:function next () {return dispatch(i + 1)}*/return yield next()// 執行完koa1的中間件,又回到了利用await執行koa2中間件的正軌
}

個人感覺koa-convert的思路就是對Generator封裝一層Promise,使上一個中間件可以利用await next()的方式調用,對于Generator的執行,利用co庫,從而達到了兼容的目的。

?

?

?

轉載于:https://www.cnblogs.com/duhuo/p/8526160.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/252226.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/252226.shtml
英文地址,請注明出處:http://en.pswp.cn/news/252226.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

極度推薦的文章和網站

推薦文章---收錄中 因為是及時雨,這里就給他放在開頭啦&#xff1a;http://www.cnblogs.com/zhuzhenwei918/p/7241971.html webpack3.0配置指南&#xff1a;http://www.cnblogs.com/caideyipi/articles/7080010.html 超酷的偽元素網站&#xff1a;https://tympanus.net/Develop…

倍道而行:選擇排序

一、為什么學了之后過段時間又會忘記了&#xff1f; 因為沒有去運用它和認為面試需要而沒有真正的重視。現在給它賦予意義&#xff1a;1.那就是基礎牢固&#xff0c;才可觸類旁通2.真正記得和隨時可以拿出手&#xff0c;那么面試可以PK掉一大批人。不然看到一個精妙的算法就學一…

如何構建一個真實的推薦系統?

AI 前線導讀&#xff1a;隨著互聯網行業的井噴式發展&#xff0c;數據規模呈現爆炸式增長。大數據中蘊含了巨大的價值&#xff0c;但同時也來了很 “信息過載” 的問題。推薦系統作為一個廣泛應用的信息過濾系統&#xff0c;在很多領域取得了巨大的成功。在電子商務上&#xff…

volatile的適用場景

介紹 把代碼塊聲明為 synchronized&#xff0c;有兩個重要后果&#xff0c;通常是指該代碼具有 原子性&#xff08;atomicity&#xff09;和 可見性&#xff08;visibility&#xff09;。 原子性意味著個時刻&#xff0c;只有一個線程能夠執行一段代碼&#xff0c;這段代碼通過…

link和@import的區別

1、link屬于HTML標簽&#xff0c;import是css提供的 2、link是HTML標簽&#xff0c;沒有兼容問題&#xff0c;而import只在IE5以上才能識別 3、頁面被加載時&#xff0c;link會同時被加載&#xff0c;而import引用的css會等到頁面加載完再加載 4、link方式的樣式的權重高于impo…

6.java 代碼塊

代碼塊 在java中用{}括起來的稱為代碼塊&#xff0c;代碼塊可分為以下四種: 普通代碼塊構造代碼塊靜態代碼塊同步代碼塊普通代碼塊 在方法或語句中出現的{}就稱為普通代碼塊。普通代碼塊和一般語句的執行順序由他們在代碼中出現的次序決定&#xff0c;先出現先執行。 普通代碼塊…

C#如何測試代碼運行時間

第一種方式&#xff1a;System.Diagnostics.Stopwatch stopwatch new Stopwatch(); stopwatch.Start(); // 開始監視代碼運行時間 // 需要測試的代碼 .... stopwatch.Stop(); // 停止監視 TimeSpan timespan stopwatch.Elapsed; // 獲取當前實例測量得出的總時間 double …

0074 幾道面試題

昨天參加了惠裝網的面試&#xff0c;有些題不會做的&#xff0c;記錄下來 switch語句能否作用在byte、long、String上 Java1.7以前&#xff1a;byte、short、int、char Java1.7開始&#xff1a;新增String 因此switch語句不能作用在long上&#xff0c;看下面代碼&#xff1a; p…

SpringBoot入門之內嵌Tomcat配置

spring boot默認web程序啟用tomcat內嵌容器tomcat&#xff0c;監聽8080端口,servletPath默認為 / 。需要用到的就是端口、上下文路徑的修改&#xff0c;在spring boot中其修改方法極其簡單&#xff0c;實例如下&#xff1a; server.port8088 server.context-path/test 啟動程序…

第二十二章:動畫(六)

復合動畫您可以混合等待和未等待的調用來創建復合動畫。 例如&#xff0c;假設您希望按鈕在大小擴展的同時旋轉360度然后收縮。ViewExtensions類定義一個方法名稱ScaleTo&#xff0c;它為Scale屬性設置動畫&#xff0c;就像RotateTo為Rotate屬性設置動畫一樣。 Button大小的擴展…

C#操作Excel總結

0. 導入命名空間&#xff1a; 1234using Microsoft.Office.Core;using Microsoft.Office.Interop.Excel;using System.IO;using System.Reflection;1. 如何打開已有excel文檔&#xff0c;或者創建一個新的excel文檔 123Application app new Application();Workbooks wbks app…

Ubuntu16.04用源安裝Nginx+PHP5.6+MySQL5.6

安裝Nginx 1、首先添加nginx_signing.key(必須&#xff0c;否則出錯) $ wget http://nginx.org/keys/nginx_signing.key$ sudo apt-key add nginx_signing.key 2、添加]Nginx](http://nginx.org/)官方提供的源 $ echo "deb http://nginx.org/packages/ubuntu/ trusty ngin…

leetcode -39組合總數

搜就完事了&#xff0c;沒想著優化。唉~太菜&#xff0c;給一個位置標記位置&#xff0c;然后通過該位置向該位置及該位置以下尋找&#xff0c;這樣不存在什么重復回去查找問題。 如果總結大于目標值&#xff0c;回溯一下&#xff0c;如果不大于繼續。 class Solution { public…

避免某個子窗體重復運行的方法(showdialog、show)

在C#中窗口的顯示有兩種方式&#xff1a;模態顯示&#xff08;showdialog&#xff09;和非模態顯示&#xff08;show&#xff09;。 二者最常見的區別是&#xff1a;模態顯示后&#xff0c;彈出窗口阻止調用窗口的所有消息響應。只有在彈出窗口結束后調用窗口才能繼續。在模態窗…

ubantu之Git使用

本文講述在Ubuntu 14.04 x64環境下&#xff0c;如何安裝Git&#xff0c;配置連接GitHub&#xff0c;并且上傳本地代碼到github。 一. 注冊Git賬戶以及創建倉庫 要想使用github第一步當然是注冊github賬號了。之后就可以創建倉庫了&#xff08;免費用戶只能建公共倉庫&#xff0…

Java中基礎數據類型分類

Java中的四類八種基本數據類型 第一類&#xff1a;整數類型 byte short int long &#xff08;int是整形&#xff0c;也屬于整數類型&#xff09; 第二類&#xff1a;浮點型 float double 第三類&#xff1a;邏輯型 boolean(它只有兩個值可取true false) 第四類&#xff1…

C#如何打包EXE程序生成setup安裝文件

C#如何打包EXE程序生成setup安裝文件作為研發人員&#xff0c;在本機上開發的winform wpf或者控制臺程序需要發給其他人測試時候&#xff0c;一般需要對其進行打包生成setup安裝文件&#xff0c;今天第一次&#xff0c;搜了下資料&#xff0c;記錄如下&#xff1a;注&#xff1…

PHP正則表達式

php正則表達示的定界符 PHP的正則表達示定界符的規定如下&#xff1a; 定界符&#xff0c;不能用a-z A-Z 0-9 其他的都可以用。必須成對出現&#xff0c;有開始就有結束。 我們來例幾個例子&#xff1a; /中間寫正則/ 正確%中間寫正則% 正確^中間寫正則^ 正確中間寫正則 正確(…

最具戲劇性的分析診斷案例——十分鐘鎖定數據庫性能“元兇”

昨天&#xff0c;正好有點空時間想看看書&#xff0c;結果&#xff0c;剛打開書&#xff0c;沒看幾個字兒&#xff0c;接到用戶電話說&#xff1a;一個庫有問題&#xff0c;希望能幫忙看下。因為我知道他們那邊也有自己的專職DBA&#xff0c;于是問&#xff1a;沒讓人給看看嗎&…

Python黑科技:在家遠程遙控公司電腦,python+微信一鍵連接!

有時候需要遠程家里的臺式機使用&#xff0c;因為我平時都是用 MAC 多&#xff0c;但是遠程喚醒只能針對局域網&#xff0c;比較麻煩&#xff0c;于是我想用微信實現遠程喚醒機器。 *注意&#xff1a;全文代碼可左右滑動查看 準備工作 本程序主要是實現遠程管理 Windows10操作系…