摘要:上篇文章寫了如何閱讀的源碼粗略的過了一下的源碼但是作為一個沒有得出一個具體的結(jié)論中間件的運行原理也不清楚這里我們再仔細的過一遍的源碼跟著例子過一遍首先還是先過一遍例子起一個服務(wù)來一個作為模塊的再封裝我們還是慢慢來挖掘它是如何封裝的吧無關(guān)的代
上篇文章寫了如何閱讀Koa的源碼, 粗略的過了一下Koa的源碼, 但是作為一個沒有得出一個具體的結(jié)論, 中間件的運行原理也不清楚, 這里我們再仔細的過一遍Koa的源碼.
跟著例子過一遍首先還是先過一遍例子
const Koa = require("koa"); const app = new Koa(); app.use(async ctx => { ctx.body = "Hello World"; }); app.listen(3000);
起一個web服務(wù), 來一個Hello World, 作為http模塊的再封裝, 我們還是慢慢來挖掘它是如何封裝的吧(無關(guān)的代碼我都會刪掉).
首先是listen:
listen(...args) { const server = http.createServer(this.callback()); return server.listen(...args); }
http模塊我們都知道 無非是http.createServer(fn).listen(port), 其中fn帶著req, res. 根據(jù)上面的封裝我們可以肯定this.callback肯定是帶著請求以及進行響應(yīng)了. 那么再來看看this.callback吧.
callback() { const fn = compose(this.middleware); const handleRequest = (req, res) => { const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return handleRequest; }
果然callback返回的函數(shù)是帶著req, res的, 那我繼續(xù)往下走看handleRequest究竟經(jīng)歷了什么, ctx大佬出現(xiàn)了, 我們在用koa的時候所有請求響應(yīng)都是掛在ctx上的, 看起來ctx是通過createContext創(chuàng)建的, 那就繼續(xù)看createContext吧:
createContext(req, res) { const context = Object.create(this.context); const request = context.request = Object.create(this.request); const response = context.response = Object.create(this.response); context.app = request.app = response.app = this; context.req = request.req = response.req = req; context.res = request.res = response.res = res; request.ctx = response.ctx = context; request.response = response; response.request = request; context.originalUrl = request.originalUrl = req.url; context.cookies = new Cookies(req, res, { keys: this.keys, secure: request.secure }); request.ip = request.ips[0] || req.socket.remoteAddress || ""; context.accept = request.accept = accepts(req); context.state = {}; return context; }
createContext比較簡單, 就是把各種有用的沒用的變量掛到context上, 代碼也很簡單, 但是因為涉及到request和response我們需要簡單看一下request.js和response.js:
module.exports = { get header() { return this.req.headers; }, //..more items }
都是很簡單獲取變量沒啥好說的, 那么回到前面callback部分, ctx創(chuàng)建好了然后調(diào)用并返回了this.handleReques, 沒啥好說的, 繼續(xù)看唄:
handleRequest(ctx, fnMiddleware) { const res = ctx.res; res.statusCode = 404; const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx); onFinished(res, onerror); return fnMiddleware(ctx).then(handleResponse).catch(onerror); }
這一部分略微復雜一點, 由上面看出來fnMiddleware 是我們?nèi)〕鰜淼闹虚g件, 然后我們把ctx傳到中間件里執(zhí)行, 跟我們的通常用法有點像了. 到這一步重點來了: 中間件
中間件在探究中間件的原理之前, 不妨先來看看中間件是怎么用的, 來個簡單的例子:
const Koa = require("koa") const app = new Koa() app.use(async function m1 (ctx, nex) { console.log("m1") await next() console.log("m2 end") }) app.use(async function m2 (ctx, nex) { console.log("m2") await next() console.log("m2 end") }) app.use(async function m3 (ctx, nex) { console.log("m3") ctx.body = "Hello World" })
上面的結(jié)果很明確了, 但是我們不妨來可視化一下:
m1: 輸出m1
await1: m1你先暫停一下讓m2先走
m1: ...
m2: 輸出m2
await2: m2你也停一下讓m3先走
m2: ...(委屈)
m3: 輸出m3, 上面的注意啦要遠路返回了
m2: 輸出m2 end m1注意了我要返回啦
m1: 輸出m1 end
respond: ctx.body是Hello world呢 就糊弄一下用戶返回吧
看到?jīng)], ctx.body不代表立即響應(yīng), 僅僅是一個我們后面會用到的變量, 也就是說我們的ctx過了一遍所有的中間件然后才會做出響應(yīng). 這里不提await神奇的暫停效果, 我們就需要可以這么用就行了. 那么我們這個中間件是怎么實現(xiàn)的呢, 來看compose.js:
function compose (middleware) { /** * @param {Object} context * @return {Promise} * @api public */ return function (context, next) { let index = -1 return dispatch(0) function dispatch (i) { index = i let fn = middleware[i] if (!fn) return Promise.resolve() return Promise.resolve(fn(context, function next () { return dispatch(i + 1) })) } } }
看過我前一篇的可以知道這里其實就是一個遞歸. 但是跟connect的遞歸不一樣這里是Promise, 我們都知道await 跟Promise搭配味道更佳嘛. 重點是這個next, 我們調(diào)用了await next之后, 程序非得等這個Promise執(zhí)行完不可, 我們來簡化一下中間件的模型:
Promise.resolve(async m1 () { console.log(m1) await Promise.resolve(async m2 () { console.log(m2) await Promise.resolve(async m3 () { console.log(m3) ctx.body = "xxx" }) console.log(m2 end) }) console.log(m1 end) })
是不是這樣一下就清楚了, 作為應(yīng)用層的東西, 我們不需要去考慮async/await究竟是怎么實現(xiàn)的, 只需要了解它實現(xiàn)了什么樣的效果.
還是得佩服tj大神. 有問題可以互相交流哈.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/92621.html
摘要:原文博客地址,歡迎學習交流點擊預覽讀了下的源碼,寫的相當?shù)木?,遇到處理中間件執(zhí)行的模塊決定學習一下這個模塊的源碼。當在下游沒有更多的中間件執(zhí)行后,堆棧將展開并且每個中間件恢復執(zhí)行其上游行為。 原文博客地址,歡迎學習交流:點擊預覽 讀了下Koa的源碼,寫的相當?shù)木?,遇到處理中間件執(zhí)行的模塊koa-Compose,決定學習一下這個模塊的源碼。 閱讀本文可以學到: Koa中間件的加載...
摘要:本筆記共四篇源碼閱讀筆記源碼閱讀筆記源碼閱讀筆記服務(wù)器啟動與請求處理源碼閱讀筆記對象起因前兩天終于把自己一直想讀的源代碼讀了一遍。首先放上關(guān)鍵的源代碼在上一篇源碼閱讀筆記服務(wù)器啟動與請求處理中,我們已經(jīng)分析了的作用。 本筆記共四篇Koa源碼閱讀筆記(1) -- coKoa源碼閱讀筆記(2) -- composeKoa源碼閱讀筆記(3) -- 服務(wù)器の啟動與請求處理Koa源碼閱讀筆記(4...
摘要:洋蔥圈處理模型?;诘撵`活強大的中間件機制。參考官網(wǎng)提供的基本,不在贅述部分實現(xiàn),參考源碼分析常用服務(wù)端口監(jiān)聽返回適用于方法的回調(diào)函數(shù)來處理請求。 本文 github 地址: https://github.com/HCThink/h-blog/blob/master/source/koa2/readme.md github 首頁(star+watch,一手動態(tài)直達): https:...
摘要:很明顯是一個構(gòu)造函數(shù)。默認為根據(jù)原生的對象生成一個對象回調(diào)函數(shù)處理服務(wù)器響應(yīng)可以看到,方法返回的函數(shù)就是方法所需要的回調(diào)函數(shù)。 koa 是什么這里不介紹了,這里通過一個小例子結(jié)合源碼講一講它的實現(xiàn)。 koa 源碼結(jié)構(gòu) 通過 npm 安裝 koa(v2.2.0) 后,代碼都在 lib 文件夾內(nèi),包括 4 個文件,application.js, context.js, request.js...
閱讀 1107·2021-11-23 09:51
閱讀 1074·2021-10-18 13:31
閱讀 2967·2021-09-22 16:06
閱讀 4256·2021-09-10 11:19
閱讀 2196·2019-08-29 17:04
閱讀 425·2019-08-29 10:55
閱讀 2472·2019-08-26 16:37
閱讀 3369·2019-08-26 13:29