摘要:的嵌套就像是洋蔥模型的形狀就是一層包裹著一層,直到到最里面一層的的值返回。中間件引擎是有模塊來實現(xiàn)的,也就是實現(xiàn)洋蔥模型的核心引擎。表示遍歷還沒有結(jié)束。
中間件特性
| | | middleware 1 | | | | +-----------------------------------------------------------+ | | | | | | | middleware 2 | | | | | | | | +---------------------------------+ | | | | | | | | | action | action | middleware 3 | action | action | | 001 | 002 | | 005 | 006 | | | | action action | | | | | | 003 004 | | | | | | | | |
+---------------------------------------------------------------------------------------------------->
| | | | | | | | | | | | | | +---------------------------------+ | | | +-----------------------------------------------------------+ | +----------------------------------------------------------------------------------+
先寫一段貫穿全文的koa的代碼
const Koa = require("koa"); let app = new Koa(); const middleware1 = async (ctx, next) => { console.log(1); await next(); console.log(6); } const middleware2 = async (ctx, next) => { console.log(2); await next(); console.log(5); } const middleware3 = async (ctx, next) => { console.log(3); await next(); console.log(4); } app.use(middleware1); app.use(middleware2); app.use(middleware3); app.use(async(ctx, next) => { ctx.body = "hello world" }) app.listen(3001) // 輸出1,2,3,4,5,6
await next()使每個middleware分成,前置操作,等待其他中間件操作可以觀察到中間件的特性有:
上下文ctx
await next()控制前后置操作
后置操作類似于數(shù)據(jù)解構(gòu)-棧,先進后出
promise 的模擬實現(xiàn)Promise.resolve(middleware1(context, async() => { return Promise.resolve(middleware2(context, async() => { return Promise.resolve(middleware3(context, async() => { return Promise.resolve(); })); })); })) .then(() => { console.log("end"); });
從這段模擬代碼我們可以知道next()返回的是promise,需要使用await去等待promise的resolve值。promise的嵌套就像是洋蔥模型的形狀就是一層包裹著一層,直到await到最里面一層的promise的resolve值返回。
思考:
如果next()不加await執(zhí)行順序是什么呢?
在這個例子里面如果只是next()執(zhí)行順序跟await next()是一樣的,因為next的前置操作是同步的
如果前置操作是異步的操作呢?
const p = function(args) { return new Promise(resolve => { setTimeout(() => { console.log(args); resolve(); }, 100); }); }; const middleware1 = async (ctx, next) => { await p(1); // await next(); next(); console.log(6); }; const middleware2 = async (ctx, next) => { await p(2); // await next(); next(); console.log(5); }; const middleware3 = async (ctx, next) => { await p(3); // await next(); next(); console.log(4); }; // 輸出結(jié)果:1,6,2,5,3,4
當程序執(zhí)行到middleware1,執(zhí)行到await p(1)等待promise值返回跳出然后到下一個事件循環(huán)時,執(zhí)行next()也就是執(zhí)行到middleware2,再執(zhí)行到await p(2)等待promise值返回跳出middleware2,回到middleware1繼續(xù)執(zhí)行console.log(6),以此類推輸出順序為1.6.2.5.3.4
Promise的嵌套雖然可以實現(xiàn)中間件流程,但是嵌套的代碼會產(chǎn)生可維護性和可讀性的問題,也帶來中間件擴展的問題。
Koa.js中間件引擎是有koa-compose模塊來實現(xiàn)的,也就是Koa.js實現(xiàn)洋蔥模型的核心引擎。
koa-compose 實現(xiàn)this.middleware = []; use(fn) { this.middleware.push(fn); …… } callback() { const fn = compose(this.middleware); …… } function compose (middleware) { return function (context, next) { let index = -1 return dispatch(0) function dispatch (i) { if (i <= index) return Promise.reject(new Error("next() called multiple times")) index = i let fn = middleware[i] if (i === middleware.length) fn = next if (!fn) return Promise.resolve() try { return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err) } } } }
Koa實現(xiàn)的代碼非常簡潔,我們在使用use的時候?qū)iddleware存在一個數(shù)組里面,當攔截到請求時執(zhí)行callback方法,callback中調(diào)用了compose,compose方法使用遞歸執(zhí)行中間件,遍歷完成返回promise.resolve(),實際最后執(zhí)行的代碼也是上面所講的promise嵌套的形式。
擴展:Await與Generator通常我們的都會說await阻塞后面的操作等待promise的resolve返回值或者其他值,如果沒有await這個語法糖,要怎么去實現(xiàn)呢?這個等待的過程是怎么控制的呢?
GeneratorGenerator實際上是一個特殊的迭代器
let gen = null; function* genDemo(){ console.log(1) yield setTimeout(()=>{ console.log(3); gen.next();// c },100) console.log(4) } gen = genDemo();// a gen.next(); // b
a. 調(diào)用generator,該函數(shù)不執(zhí)行,也就是還沒有輸出1,返回的是指向內(nèi)部狀態(tài)的遍歷對象。
b. generator函數(shù)開始執(zhí)行,輸出1,遇到第一個yeild表達式停下來,調(diào)用gen.next()返回一個對象{value: 10, done:false},這里的value表示setTimeout的一個標識值,也就是調(diào)用clearTimeout的參數(shù),是一個數(shù)字。done表示遍歷還沒有結(jié)束。100毫秒后輸出3;
c. Generator函數(shù)從上次在yeild停止的地方一直執(zhí)行到函數(shù)結(jié)束(沒有其他的yeild),輸出4,返回{value: undefined,done:true},表示遍歷結(jié)束。
可以看到y(tǒng)eild有控制代碼進度的作用,是不是跟await有異曲同工之妙
來看下await編譯成generator形式的代碼,雖然多了一些代碼,但是我們可以把_asyncToGenerator(function*() {……}調(diào)用generator,把asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);看成是gen.next();就很容易理解了。
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } const middleware1 = /*#__PURE__*/ (function() { var _ref = _asyncToGenerator(function*(ctx, next) { console.log(1); yield next(); console.log(6); }); return function middleware1(_x, _x2) { return _ref.apply(this, arguments); }; })(); const middleware2 = /*#__PURE__*/ (function() { var _ref2 = _asyncToGenerator(function*(ctx, next) { console.log(2); yield next(); console.log(5); }); return function middleware2(_x3, _x4) { return _ref2.apply(this, arguments); }; })(); const middleware3 = /*#__PURE__*/ (function() { var _ref3 = _asyncToGenerator(function*(ctx, next) { console.log(3); yield next(); console.log(4); }); return function middleware3(_x5, _x6) { return _ref3.apply(this, arguments); }; })(); Promise.resolve( middleware1( context, /*#__PURE__*/ _asyncToGenerator(function*() { return Promise.resolve( middleware2( context, /*#__PURE__*/ _asyncToGenerator(function*() { return Promise.resolve( middleware3( context, /*#__PURE__*/ _asyncToGenerator(function*() { return Promise.resolve(); }) ) ); }) ) ); }) ) ).then(() => { console.log("end"); });
參考鏈接:
https://chenshenhai.github.io...
https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/106100.html
摘要:實現(xiàn)的四大模塊上文簡述了源碼的大體框架結(jié)構(gòu),接下來我們來實現(xiàn)一個的框架,筆者認為理解和實現(xiàn)一個框架需要實現(xiàn)四個大模塊,分別是封裝創(chuàng)建類構(gòu)造函數(shù)構(gòu)造對象中間件機制和剝洋蔥模型的實現(xiàn)錯誤捕獲和錯誤處理下面我們就逐一分析和實現(xiàn)。 什么是koa框架? ? ? ? ?koa是一個基于node實現(xiàn)的一個新的web框架,它是由express框架的原班人馬打造的。它的特點是優(yōu)雅、簡潔、表達力強、自由度...
摘要:實現(xiàn)的四大模塊上文簡述了源碼的大體框架結(jié)構(gòu),接下來我們來實現(xiàn)一個的框架,筆者認為理解和實現(xiàn)一個框架需要實現(xiàn)四個大模塊,分別是封裝創(chuàng)建類構(gòu)造函數(shù)構(gòu)造對象中間件機制和剝洋蔥模型的實現(xiàn)錯誤捕獲和錯誤處理下面我們就逐一分析和實現(xiàn)。 什么是koa框架? ? ? ? ?koa是一個基于node實現(xiàn)的一個新的web框架,它是由express框架的原班人馬打造的。它的特點是優(yōu)雅、簡潔、表達力強、自由度...
摘要:最近讀了的源碼,理清楚了架構(gòu)設(shè)計與用到的第三方庫。本系列將分為篇,分別介紹的架構(gòu)設(shè)計和個核心庫,最終會手動實現(xiàn)一個簡易的。本文來自心譚博客深入源碼核心庫原理所有系列文章都放在了。這一段邏輯封裝在了核心庫里面。 最近讀了 koa2 的源碼,理清楚了架構(gòu)設(shè)計與用到的第三方庫。本系列將分為 3 篇,分別介紹 koa 的架構(gòu)設(shè)計和 3 個核心庫,最終會手動實現(xiàn)一個簡易的 koa。這是系列第 2...
摘要:到此為止,我們就基本講清楚了中的中間件洋蔥模型是如何自動執(zhí)行的。 koa被認為是第二代web后端開發(fā)框架,相比于前代express而言,其最大的特色無疑就是解決了回調(diào)金字塔的問題,讓異步的寫法更加的簡潔。在使用koa的過程中,其實一直比較好奇koa內(nèi)部的實現(xiàn)機理。最近終于有空,比較深入的研究了一下koa一些原理,在這里會寫一系列文章來記錄一下我的學習心得和理解。 在我看來,koa最核心...
閱讀 2152·2023-04-26 00:23
閱讀 826·2021-09-08 09:45
閱讀 2446·2019-08-28 18:20
閱讀 2553·2019-08-26 13:51
閱讀 1606·2019-08-26 10:32
閱讀 1405·2019-08-26 10:24
閱讀 2041·2019-08-26 10:23
閱讀 2208·2019-08-23 18:10