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