洋葱模型php,理解Koa洋葱模型
中間件特性
| |
| 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()使每個(gè)middleware分成,前置操作,等待其他中間件操作可以觀察到中間件的特性有:
上下文ctx
await next()控制前后置操作
后置操作類似于數(shù)據(jù)解構(gòu)-棧,先進(jìn)后出
promise 的模擬實(shí)現(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í)行順序是什么呢?
在這個(gè)例子里面如果只是next()執(zhí)行順序跟await next()是一樣的,因?yàn)閚ext的前置操作是同步的
如果前置操作是異步的操作呢?
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
當(dāng)程序執(zhí)行到middleware1,執(zhí)行到await p(1)等待promise值返回跳出然后到下一個(gè)事件循環(huán)時(shí),執(zhí)行next()也就是執(zhí)行到middleware2,再執(zhí)行到await p(2)等待promise值返回跳出middleware2,回到middleware1繼續(xù)執(zhí)行console.log(6),以此類推輸出順序?yàn)?.6.2.5.3.4
Promise的嵌套雖然可以實(shí)現(xiàn)中間件流程,但是嵌套的代碼會(huì)產(chǎn)生可維護(hù)性和可讀性的問(wèn)題,也帶來(lái)中間件擴(kuò)展的問(wèn)題。
Koa.js中間件引擎是有koa-compose模塊來(lái)實(shí)現(xiàn)的,也就是Koa.js實(shí)現(xiàn)洋蔥模型的核心引擎。
koa-compose 實(shí)現(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實(shí)現(xiàn)的代碼非常簡(jiǎn)潔,我們?cè)谑褂胾se的時(shí)候?qū)iddleware存在一個(gè)數(shù)組里面,當(dāng)攔截到請(qǐng)求時(shí)執(zhí)行callback方法,callback中調(diào)用了compose,compose方法使用遞歸執(zhí)行中間件,遍歷完成返回promise.resolve(),實(shí)際最后執(zhí)行的代碼也是上面所講的promise嵌套的形式。
擴(kuò)展:Await與Generator
通常我們的都會(huì)說(shuō)await阻塞后面的操作等待promise的resolve返回值或者其他值,如果沒(méi)有await這個(gè)語(yǔ)法糖,要怎么去實(shí)現(xiàn)呢?這個(gè)等待的過(guò)程是怎么控制的呢?
Generator
Generator實(shí)際上是一個(gè)特殊的迭代器
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í)行,也就是還沒(méi)有輸出1,返回的是指向內(nèi)部狀態(tài)的遍歷對(duì)象。
b. generator函數(shù)開始執(zhí)行,輸出1,遇到第一個(gè)yeild表達(dá)式停下來(lái),調(diào)用gen.next()返回一個(gè)對(duì)象{value: 10, done:false},這里的value表示setTimeout的一個(gè)標(biāo)識(shí)值,也就是調(diào)用clearTimeout的參數(shù),是一個(gè)數(shù)字。done表示遍歷還沒(méi)有結(jié)束。100毫秒后輸出3;
c. Generator函數(shù)從上次在yeild停止的地方一直執(zhí)行到函數(shù)結(jié)束(沒(méi)有其他的yeild),輸出4,返回{value: undefined,done:true},表示遍歷結(jié)束。
可以看到y(tǒng)eild有控制代碼進(jìn)度的作用,是不是跟await有異曲同工之妙
來(lái)看下await編譯成generator形式的代碼,雖然多了一些代碼,但是我們可以把_asyncToGenerator(function*() {……}調(diào)用generator,把a(bǔ)syncGeneratorStep(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");
});
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的洋葱模型php,理解Koa洋葱模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: php语言的四种循环控制语句,PHP 控
- 下一篇: php ayui表格,layui表格使用