深入koa原始碼(二) 核心庫原理

2021-09-24 17:13:22 字數 4350 閱讀 1474

最近讀了 koa2 的原始碼,理清楚了架構設計與用到的第三方庫。本系列將分為 3 篇,分別介紹 koa 的架構設計和 3 個核心庫,最終會手動實現乙個簡易的 koa。這是系列第 2 篇,關於 3 個核心庫的原理

所有系列文章都放在了github。歡迎交流和star✿✿ ヽ(°▽°)ノ ✿

koa2 種推薦使用 async 函式,koa1 推薦的是 generator。koa2 為了相容,在呼叫use新增中介軟體的時候,會判斷是否是 generator。如果是,則用covert庫轉化為 async 函式。

判斷是不是 generator 的邏輯寫在了 is-generator-function 庫中,邏輯非常簡單,通過判斷object.prototype.tostring.call的返回結果即可:

function

*say()

object.prototype.tostring.

call

(say)

;// 輸出: [object generatorfunction]

delegates和 koa 一樣,這個庫都是出自大佬 tj 之手。它的作用就是屬性**。這個**庫常用的方法有gettersettermethodaccess

假設準備了乙個物件target,為了方便訪問其上request屬性的內容,對request進行**:

const delegates =

require

("delegates");

const target =}}

;delegates

(target,

"request").

getter

("name").

setter

("name").

method

("say"

);

**後,訪問request將會更加方便:

console.

log(target.name)

;// xintan

target.name =

"xintan!!!"

;console.

log(target.name)

;// xintan!!!

target.

say();

// hello

對於settergetter方法,是通過呼叫物件上的__definesetter____definegetter__來實現的。下面是單獨拿出來的邏輯:

/**

* @param proto 被**物件

* @param property 被**物件上的被**屬性

* @param name

*/function

mydelegates

(proto, property, name));

proto.

__definesetter__

(name,

function

(val));

}mydelegates

(target,

"request"

,"name");

console.

log(target.name)

;// xintan

target.name =

"xintan!!!"

;console.

log(target.name)

;// xintan!!!

剛開始我的想法是更簡單一些,就是直接讓proto[name] = proto[property][name]。但這樣做有個缺點無法彌補,就是之後如果proto[property][name]改變,proto[name]獲取不了最新的值。

對於method方法,實現上是在物件上建立了新屬性,屬性值是乙個函式。這個函式呼叫的就是**目標的函式。下面是單獨拿出來的邏輯:

/**

* * @param proto 被**物件

* @param property 被**物件上的被**屬性

* @param method 函式名

*/function

mydelegates

(proto, property, method);}

mydelegates

(target,

"request"

,"say");

target.

say();

// hello

koa 中也有對屬性的access方法**,這個方法就是gettersetter寫在一起的語法糖。

koa 最讓人驚豔的就是大名鼎鼎的「洋蔥模型」。以至於之前我在開發 koa 中介軟體的時候,一直有種 magic 的方法。經常疑惑,這裡await next(),執行完之後的中介軟體又會重新回來繼續執行未執行的邏輯。

這一段邏輯封裝在了核心庫koa-compose 裡面。原始碼也很簡單,算上各種注釋只有不到 50 行。為了方便說明和理解,我把其中一些意外情況檢查的**去掉:

function

compose

(middleware)

catch

(err)}

};}

middleware 裡面儲存的就是開發者自定義的中介軟體處理邏輯。為了方便說明,我準備了 2 個中介軟體函式:

const middleware =

[async

(ctx, next)

=>

,async

(ctx, next)

=>

];

現在,模擬在 koa 中對 compose 函式的呼叫,我們希望程式的輸出是:a b c(正如使用 koa 那樣)。執行以下**即可:

const fns =

compose

(middleware)

;fns()

;

ok,目前已經模擬出來了乙個不考慮異常情況的洋蔥模型了。

為什麼會有洋蔥穿透的的效果呢?回到上述的compose函式,閉包寫法返回了乙個新的函式,其實就是返回內部定義的dispatch函式。其中,引數的含義分別是:

在上面的測試用例中,fns其實就是dispatch(0)。在dispatch函式中,通過引數 i 拿到了當前要執行的中介軟體fn

return promise.

resolve(fn

(context, dispatch.

bind

(null

, i +1)

));

那麼,在中介軟體中執行await next(),其實就是執行:await dispatch.bind(null, i + 1)。因此看起來,當前中介軟體會停止自己的邏輯,先處理下乙個中介軟體的邏輯。

因為每個dispatch,都返回新的 promsise。所以async會等到 promise 狀態改變後再回來繼續執行自己的邏輯。

最後,在不考慮 koa 的上下文環境的情況下,用 async/await 的提煉出了 compose 函式:

function

compose

(middleware)

catch

(err)}

}

下面是它的使用方法:

const middleware =

[async next =>

,async next =>];

compose

(middleware)

;// 輸出a b c

希望最後這段**能幫助理解!

Koa原始碼分析(二) co的實現

koa原始碼分析 一 generator koa原始碼分析 二 co的實現 koa原始碼分析 三 middleware機制的實現 大名鼎鼎的co是什麼?它是tj大神基於es6的一些新特性開發的非同步流程控制庫,基於它所開發的koa被視為未來主流的web框架。koa基於co實現,而co又是使用了es6...

springmvc核心原理及原始碼分析

1 dispatherservlet顧名思義乙個排程的servlet,是乙個front controller 前端控制器 也可以說是springmvc的c位,負責接受客戶端的request,並將這些request分配給對應的處理元件 3.dispatcherservlet 根據獲得的handler,...

深入剖析PHP7核心原始碼(二) PHP變數容器

php的變數使用起來非常方便,其基本結構是底層實現的zval,php7採用了全新的zval,由此帶來了非常大的效能提公升,本文重點分析php7的zval的改變。typedef struct zval struct zval typedef union zvalue value str hashtab...