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