使用異步編程保證 Koa 的洋蔥模型
哈嘍大家好!我是小三。今天更的是前端文章,小三前端比較菜,有什么地方寫得不對(duì)大家可以留言或者聯(lián)系我探討修改哦。
koa框架的業(yè)務(wù)流程是一個(gè)完全的異步編程模型,通過(guò)ctx上下文對(duì)象來(lái)貫穿http的上下游。對(duì)我們來(lái)說(shuō)最重要的就是理解洋蔥模型。
先來(lái)看一個(gè)經(jīng)典的洋蔥圖認(rèn)識(shí)一下
我們先來(lái)看一下這個(gè)代碼
- const Koa = require('koa')
- const app = new Koa()
- //第一個(gè)中間件
- app.use((ctx, next) => {
- console.log("第一個(gè)中間件", 1);
- next()
- console.log("第一個(gè)中間件", 2);
- })
- //第二個(gè)中間件
- app.use((ctx, next) => {
- console.log("第二個(gè)中間件", 3);
- next()
- console.log("第二個(gè)中間件", 4);
- })
- //第三個(gè)中間件
- app.use((ctx, next) => {
- console.log("第三個(gè)中間件", 5);
- console.log("第三個(gè)中間件", 6);
- })
- app.listen(3000, () => {
- console.log("Koa已經(jīng)開啟在http://loclhost:3000");
- })
我們運(yùn)行這個(gè)代碼在瀏覽器打開并返回控制臺(tái)看一下打印
第一個(gè)中間件 1
第二個(gè)中間件 3
第三個(gè)中間件 5
第三個(gè)中間件 6
第二個(gè)中間件 4
第一個(gè)中間件 2
大家可以看這段代碼,其執(zhí)行效果為135642,也就是說(shuō)這就好比第一個(gè)中間件把第二個(gè)包裹了起來(lái),第三個(gè)中間件又把第二個(gè)中間件包起來(lái)了,調(diào)用next時(shí)就回去執(zhí)行第二個(gè)中間件,結(jié)束后繼續(xù)執(zhí)行第一個(gè)。
所以他的順序應(yīng)該是這樣的
看到上圖相信大家已經(jīng)非常的了解了吧。
然后下面我們會(huì)用到async await這個(gè)語(yǔ)法糖,我在這里簡(jiǎn)單介紹一下async函數(shù)
它是generator函數(shù)的語(yǔ)法糖,可以通過(guò) yield(中文翻譯動(dòng)詞為提供,暫時(shí)叫他提供) 關(guān)鍵字,就是把函數(shù)的執(zhí)行流掛起,為改變執(zhí)行流程提供了可能,從而為異步編程提供解決方案。
async函數(shù),就是將generator函數(shù)的*換成async,將yield替換成await
簡(jiǎn)單來(lái)說(shuō)async/await,就是異步編程回調(diào)函數(shù)寫法的替代方法,暫且就說(shuō)這么多,下一篇文章我再詳細(xì)介紹async await函數(shù),
再多說(shuō)一句 async 函數(shù)執(zhí)行時(shí),如果遇到 await 就會(huì)先暫停執(zhí)行 ,等到觸發(fā)的異步操作完成后,恢復(fù) async 函數(shù)的執(zhí)行并返回解析值。
我們?cè)俜从^這個(gè)洋蔥模型
然后我再在第三個(gè)中間件加了個(gè)axios請(qǐng)求,因?yàn)樗钱惒降牟僮?,所以我得再在前面加個(gè)async,然后再在請(qǐng)求的前面加個(gè)await,這樣我們就可以得到get請(qǐng)求的這個(gè)結(jié)果,如果不加,他返回的是一個(gè)Promise對(duì)象
這里是加了async await函數(shù)的,但是.....
- const Koa = require('koa')
- const app = new Koa()
- //第一個(gè)中間件
- app.use((ctx, next) => {
- console.log("第一個(gè)中間件", 1);
- next()
- console.log("第一個(gè)中間件", 2);
- })
- //第二個(gè)中間件
- app.use((ctx, next) => {
- console.log("第二個(gè)中間件", 3);
- next()
- console.log("第二個(gè)中間件", 4);
- })
- //第三個(gè)中間件
- app.use(async(ctx, next) => {
- console.log("第三個(gè)中間件", 5);
- const axios = require("axios")
- const res = await axios.get('http://www.baidu.com')
- console.log(res);
- console.log('發(fā)送了axios請(qǐng)求');
- console.log("第三個(gè)中間件", 6);
- })
- app.listen(3000, () => {
- console.log("Koa已經(jīng)開啟在http://localhost:3000");
- })
我們自行打印這個(gè)結(jié)果,可以看到
中間省略....
可以看到,我們雖然取回了這個(gè)res結(jié)果,但是它的打印順序變了,也就是它遇到await后就會(huì)先暫停執(zhí)行 ,等到觸發(fā)的異步操作完成后,恢復(fù) async 函數(shù)的執(zhí)行并返回解析值。
但是這不符合我們想要的結(jié)果,我們想要的是它按照本來(lái)的順序執(zhí)行,
然后我們?yōu)榱吮WC洋蔥模型,我們應(yīng)該如下改動(dòng),將前面的代碼也添加async await用來(lái)控制情況在我們預(yù)期之內(nèi)。
- const Koa = require('koa')
- const app = new Koa()
- //第一個(gè)中間件
- app.use(async(ctx, next) => {
- console.log("第一個(gè)中間件", 1);
- await next()
- console.log("第一個(gè)中間件", 2);
- })
- //第二個(gè)中間件
- app.use(async(ctx, next) => {
- console.log("第二個(gè)中間件", 3);
- await next()
- console.log("第二個(gè)中間件", 4);
- })
- //第三個(gè)中間件
- app.use(async(ctx, next) => {
- console.log("第三個(gè)中間件", 5);
- const axios = require("axios")
- const res = await axios.get('http://www.baidu.com')
- console.log('發(fā)送了axios請(qǐng)求');
- console.log("第三個(gè)中間件", 6);
- })
- app.listen(3000, () => {
- console.log("Koa已經(jīng)開啟在http://loclhost:3000");
- })
運(yùn)行代碼我們可以看到
中間省略...
所以我們?cè)趯懼虚g件函數(shù)的時(shí)候,一般都將中間件變成async await函數(shù),這樣就不會(huì)因?yàn)楫惒骄幊虒?dǎo)致洋蔥模型不可控以至于不合理
以上是我自己的理解,如果有更多的比如我說(shuō)不清的,可以留言告訴我,我會(huì)好好去學(xué)習(xí),大家一起把問(wèn)題說(shuō)出來(lái)互相學(xué)習(xí),希望大家不要吝嗇,求求各位大佬了